chore: Initial Commit

feat: Gestion des inscriptions [#1]
feat(frontend): Création des vues pour le paramétrage de l'école [#2]
feat: Gestion du login [#6]
fix: Correction lors de la migration des modèle [#8]
feat: Révision du menu principal [#9]
feat: Ajout d'un footer [#10]
feat: Création des dockers compose pour les environnements de
développement et de production [#12]
doc(ci): Mise en place de Husky et d'un suivi de version automatique [#14]
This commit is contained in:
Luc SORIGNET
2024-11-18 10:02:58 +01:00
committed by N3WT DE COMPET
commit af0cd1c840
228 changed files with 22694 additions and 0 deletions

View File

@ -0,0 +1,124 @@
'use client'
import React, { useState, useEffect } from 'react'
import DjangoCSRFToken from '@/components/DjangoCSRFToken'
import Logo from '@/components/Logo';
import { useSearchParams, useRouter } from 'next/navigation'
import InputTextIcon from '@/components/InputTextIcon';
import Loader from '@/components/Loader'; // Importez le composant Loader
import Button from '@/components/Button'; // Importez le composant Button
import { User, KeySquare } from 'lucide-react'; // Importez directement les icônes nécessaires
import { BK_LOGIN_URL, FR_ADMIN_STUDENT_EDIT_SUBSCRIBE, FR_PARENTS_HOME_URL, FR_USERS_NEW_PASSWORD_URL, FR_USERS_SUBSCRIBE_URL } from '@/utils/Url';
import useLocalStorage from '@/hooks/useLocalStorage';
import useCsrfToken from '@/hooks/useCsrfToken';
const useFakeData = process.env.NEXT_PUBLIC_USE_FAKE_DATA === 'true';
export default function Page() {
const searchParams = useSearchParams();
const [errorMessage, setErrorMessage] = useState("");
const [userFieldError,setUserFieldError] = useState("")
const [passwordFieldError,setPasswordFieldError] = useState("")
const [isLoading, setIsLoading] = useState(false);
const [userId, setUserId] = useLocalStorage("userId", '') ;
const router = useRouter();
const csrfToken = useCsrfToken();
function isOK(data) {
return data.errorMessage === ""
}
function handleFormLogin(formData) {
if (useFakeData) {
// Simuler une réponse réussie
const data = {
errorFields: {},
errorMessage: "",
profil: "fakeProfileId"
};
setUserFieldError("")
setPasswordFieldError("")
setErrorMessage("")
if(isOK(data)){
localStorage.setItem('userId', data.profil); // Stocker l'identifiant de l'utilisateur
router.push(`${FR_ADMIN_STUDENT_EDIT_SUBSCRIBE}?id=${data.profil}`);
} else {
if(data.errorFields){
setUserFieldError(data.errorFields.email)
setPasswordFieldError(data.errorFields.password);
}
if(data.errorMessage){
setErrorMessage(data.errorMessage)
}
}
} else {
const request = new Request(
`${BK_LOGIN_URL}`,
{
method:'POST',
headers: {
'Content-Type':'application/json',
'X-CSRFToken': csrfToken
},
body: JSON.stringify( {
email: formData.get('login'),
password: formData.get('password'),
}),
credentials: 'include',
}
);
fetch(request).then(response => response.json())
.then(data => {
console.log('Success:', data);
setUserFieldError("")
setPasswordFieldError("")
setErrorMessage("")
if(isOK(data)){
localStorage.setItem('userId', data.profil); // Stocker l'identifiant de l'utilisateur
router.push(`${FR_PARENTS_HOME_URL}`);
} else {
if(data.errorFields){
setUserFieldError(data.errorFields.email)
setPasswordFieldError(data.errorFields.password);
}
if(data.errorMessage){
setErrorMessage(data.errorMessage)
}
}
})
.catch(error => {
console.error('Error fetching data:', error);
error = error.message;
console.log(error);
});
}
}
if (isLoading === true) {
return <Loader /> // Affichez le composant Loader
} else {
return <>
<div className="container max mx-auto p-4">
<div className="flex justify-center mb-4">
<Logo className="h-150 w-150" />
</div>
<h1 className="text-2xl text-emerald-900 font-bold text-center mb-4">Authentification</h1>
<form className="max-w-md mx-auto" onSubmit={(e) => { e.preventDefault(); handleFormLogin(new FormData(e.target)); }}>
<DjangoCSRFToken csrfToken={csrfToken} />
<InputTextIcon name="login" type="text" IconItem={User} label="Identifiant" placeholder="Identifiant" errorMsg={userFieldError} className="w-full" />
<InputTextIcon name="password" type="password" IconItem={KeySquare} label="Mot de passe" placeholder="Mot de passe" errorMsg={passwordFieldError} className="w-full" />
<div className="input-group mb-4">
</div>
<label className="text-red-500">{errorMessage}</label>
<label><a className="float-right text-emerald-900" href={`${FR_USERS_NEW_PASSWORD_URL}`}>Mot de passe oublié ?</a></label>
<div className="form-group-submit mt-4">
<Button text="Se Connecter" className="w-full" primary type="submit" name="connect" />
</div>
</form>
</div>
</>
}
};

View File

@ -0,0 +1,107 @@
'use client'
import React, { useState, useEffect } from 'react';
import DjangoCSRFToken from '@/components/DjangoCSRFToken'
import Logo from '@/components/Logo';
import { useSearchParams, useRouter } from 'next/navigation';
import InputTextIcon from '@/components/InputTextIcon';
import Loader from '@/components/Loader'; // Importez le composant Loader
import Button from '@/components/Button'; // Importez le composant Button
import Popup from '@/components/Popup'; // Importez le composant Popup
import { User } from 'lucide-react'; // Importez directement les icônes nécessaires
import { BK_NEW_PASSWORD_URL,FR_USERS_LOGIN_URL } from '@/utils/Url';
import useCsrfToken from '@/hooks/useCsrfToken';
const useFakeData = process.env.NEXT_PUBLIC_USE_FAKE_DATA === 'true';
export default function Page() {
const searchParams = useSearchParams();
const [errorMessage, setErrorMessage] = useState("");
const [userFieldError, setUserFieldError] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [popupVisible, setPopupVisible] = useState(false);
const [popupMessage, setPopupMessage] = useState("");
const [popupConfirmAction, setPopupConfirmAction] = useState(null);
const csrfToken = useCsrfToken();
function validate(formData) {
if (useFakeData) {
setTimeout(() => {
setUserFieldError("");
setErrorMessage("");
setPopupMessage("Mot de passe réinitialisé avec succès !");
setPopupConfirmAction(() => () => setPopupVisible(false));
setPopupVisible(true);
}, 1000); // Simule un délai de traitement
} else {
const request = new Request(
`${BK_NEW_PASSWORD_URL}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken
},
credentials: 'include',
body: JSON.stringify({
email: formData.get('email')
}),
}
);
fetch(request).then(response => response.json())
.then(data => {
console.log('Success:', data);
setUserFieldError("");
setErrorMessage("");
if (data.errorMessage === "") {
setPopupMessage(data.message);
setPopupConfirmAction(() => () => setPopupVisible(false));
setPopupVisible(true);
} else {
if (data.errorFields) {
setUserFieldError(data.errorFields.email);
}
if (data.errorMessage) {
setErrorMessage(data.errorMessage);
}
}
})
.catch(error => {
console.error('Error fetching data:', error);
error = error.errorMessage;
console.log(error);
});
}
}
if (isLoading === true) {
return <Loader /> // Affichez le composant Loader
} else {
return <>
<div className="container max mx-auto p-4">
<div className="flex justify-center mb-4">
<Logo className="h-150 w-150" />
</div>
<h1 className="text-2xl font-bold text-center mb-4">Nouveau Mot de passe</h1>
<form className="max-w-md mx-auto" onSubmit={(e) => { e.preventDefault(); validate(new FormData(e.target)); }}>
<DjangoCSRFToken csrfToken={csrfToken} />
<InputTextIcon name="email" type="text" IconItem={User} label="Identifiant" placeholder="Identifiant" errorMsg={userFieldError} className="w-full" />
<p className="text-red-500">{errorMessage}</p>
<div className="form-group-submit mt-4">
<Button text="Réinitialiser" className="w-full" primary type="submit" name="validate" />
</div>
</form>
<br />
<div className='flex justify-center mt-2 max-w-md mx-auto'>
<Button text="Annuler" className="w-full" href={ `${FR_USERS_LOGIN_URL}`} />
</div>
</div>
<Popup
visible={popupVisible}
message={popupMessage}
onConfirm={popupConfirmAction}
onCancel={() => setPopupVisible(false)}
/>
</>
}
}

View File

@ -0,0 +1,144 @@
'use client'
// src/app/pages/subscribe.js
import React, { useState, useEffect } from 'react';
import DjangoCSRFToken from '@/components/DjangoCSRFToken'
import Logo from '@/components/Logo';
import { useSearchParams, useRouter } from 'next/navigation'
import InputTextIcon from '@/components/InputTextIcon';
import Loader from '@/components/Loader'; // Importez le composant Loader
import Button from '@/components/Button'; // Importez le composant Button
import Popup from '@/components/Popup';
import { BK_RESET_PASSWORD_URL, FR_USERS_LOGIN_URL } from '@/utils/Url';
import { KeySquare } from 'lucide-react'; // Importez directement les icônes nécessaires
import useCsrfToken from '@/hooks/useCsrfToken';
const useFakeData = process.env.NEXT_PUBLIC_USE_FAKE_DATA === 'true';
export default function Page() {
const searchParams = useSearchParams();
const uuid = searchParams.get('uuid');
const [errorMessage, setErrorMessage] = useState("");
const [password1FieldError,setPassword1FieldError] = useState("")
const [password2FieldError,setPassword2FieldError] = useState("")
const [isLoading, setIsLoading] = useState(true);
const [popupVisible, setPopupVisible] = useState(false);
const [popupMessage, setPopupMessage] = useState("");
const router = useRouter();
const csrfToken = useCsrfToken();
useEffect(() => {
if (useFakeData) {
setTimeout(() => {
setIsLoading(false);
}, 1000);
} else {
const url= `${BK_RESET_PASSWORD_URL}/${uuid}`;
fetch(url, {
headers: {
'Content-Type': 'application/json',
},
}).then(response => response.json())
.then(data => {
console.log('Success:', data);
setIsLoading(true);
if(data.errorFields){
setPassword1FieldError(data.errorFields.password1)
setPassword2FieldError(data.errorFields.password2)
}
if(data.errorMessage){
setErrorMessage(data.errorMessage)
}
setIsLoading(false);
})
.catch(error => {
console.error('Error fetching data:', error);
});
}
}, []);
function validate(formData) {
if (useFakeData) {
setTimeout(() => {
setPopupMessage("Mot de passe réinitialisé avec succès");
setPopupVisible(true);
}, 1000);
} else {
const request = new Request(
`${BK_RESET_PASSWORD_URL}/${uuid}`,
{
method:'POST',
headers: {
'Content-Type':'application/json',
'X-CSRFToken': csrfToken
},
credentials: 'include',
body: JSON.stringify( {
password1: formData.get('password1'),
password2: formData.get('password2'),
}),
}
);
fetch(request).then(response => response.json())
.then(data => {
console.log('Success:', data);
setPassword1FieldError("")
setPassword2FieldError("")
setErrorMessage("")
if(data.errorMessage === ""){
setPopupMessage(data.message);
setPopupVisible(true);
} else {
if(data.errorMessage){
setErrorMessage(data.errorMessage);
}
if(data.errorFields){
setPassword1FieldError(data.errorFields.password1)
setPassword2FieldError(data.errorFields.password2)
}
}
})
.catch(error => {
console.error('Error fetching data:', error);
error = error.errorMessage;
console.log(error);
});
}
}
if (isLoading === true) {
return <Loader /> // Affichez le composant Loader
} else {
return <>
<Popup
visible={popupVisible}
message={popupMessage}
onConfirm={() => {
setPopupVisible(false);
router.push(`${FR_USERS_LOGIN_URL}`);
}}
onCancel={() => setPopupVisible(false)}
/>
<div className="container max mx-auto p-4">
<div className="flex justify-center mb-4">
<Logo className="h-150 w-150" />
</div>
<h1 className="text-2xl font-bold text-center mb-4">Réinitialisation du mot de passe</h1>
<form className="max-w-md mx-auto" onSubmit={(e) => { e.preventDefault(); validate(new FormData(e.target)); }}>
<DjangoCSRFToken csrfToken={csrfToken} />
<InputTextIcon name="password1" type="password" IconItem={KeySquare} label="Mot de passe" placeholder="Mot de passe" errorMsg={password1FieldError} className="w-full" />
<InputTextIcon name="password2" type="password" IconItem={KeySquare} label="Confirmation mot de passe" placeholder="Confirmation mot de passe" errorMsg={password2FieldError} className="w-full" />
<label className="text-red-500">{errorMessage}</label>
<div className="form-group-submit mt-4">
<Button text="Enregistrer" className="w-full" primary type="submit" name="validate" />
</div>
</form>
<br/>
<div className="flex justify-center mt-2 max-w-md mx-auto">
<Button text="Annuler" className="w-full" href={`${FR_USERS_LOGIN_URL}`} />
</div>
</div>
</>
}
}

View File

@ -0,0 +1,180 @@
'use client'
// src/app/pages/subscribe.js
import React, { useState, useEffect } from 'react';
import DjangoCSRFToken from '@/components/DjangoCSRFToken'
import Logo from '@/components/Logo';
import { useSearchParams, useRouter } from 'next/navigation'
import InputTextIcon from '@/components/InputTextIcon';
import Loader from '@/components/Loader'; // Importez le composant Loader
import Button from '@/components/Button'; // Importez le composant Button
import Popup from '@/components/Popup'; // Importez le composant Popup
import { User, KeySquare } from 'lucide-react'; // Importez directement les icônes nécessaires
import { BK_REGISTER_URL, FR_USERS_LOGIN_URL } from '@/utils/Url';
import useCsrfToken from '@/hooks/useCsrfToken';
const useFakeData = process.env.NEXT_PUBLIC_USE_FAKE_DATA === 'true';
export default function Page() {
const searchParams = useSearchParams();
const [errorMessage, setErrorMessage] = useState("");
const [userFieldError,setUserFieldError] = useState("")
const [password1FieldError,setPassword1FieldError] = useState("")
const [password2FieldError,setPassword2FieldError] = useState("")
const [isLoading, setIsLoading] = useState(true);
const [popupVisible, setPopupVisible] = useState(false);
const [popupMessage, setPopupMessage] = useState("");
const router = useRouter();
const csrfToken = useCsrfToken();
useEffect(() => {
if (useFakeData) {
// Simuler une réponse réussie
const data = {
errorFields: {},
errorMessage: ""
};
setUserFieldError("")
setPassword1FieldError("")
setPassword2FieldError("")
setErrorMessage("")
setIsLoading(false);
} else {
const url= `${BK_REGISTER_URL}`;
fetch(url, {
headers: {
'Content-Type': 'application/json',
},
}).then(response => response.json())
.then(data => {
console.log('Success:', data);
setUserFieldError("")
setPassword1FieldError("")
setPassword2FieldError("")
setErrorMessage("")
setIsLoading(true);
if(data.errorFields){
setUserFieldError(data.errorFields.email)
setPassword1FieldError(data.errorFields.password1)
setPassword2FieldError(data.errorFields.password2)
}
if(data.errorMessage){
setErrorMessage(data.errorMessage)
}
setIsLoading(false);
})
.catch(error => {
console.error('Error fetching data:', error);
});
}
}, []);
function isOK(data) {
return data.errorMessage === ""
}
function suscribe(formData) {
if (useFakeData) {
// Simuler une réponse réussie
const data = {
errorFields: {},
errorMessage: ""
};
setUserFieldError("")
setPassword1FieldError("")
setPassword2FieldError("")
setErrorMessage("")
if(isOK(data)){
setPopupMessage("Votre compte a été créé avec succès");
setPopupVisible(true);
} else {
if(data.errorMessage){
setErrorMessage(data.errorMessage);
}
if(data.errorFields){
setUserFieldError(data.errorFields.email)
setPassword1FieldError(data.errorFields.password1)
setPassword2FieldError(data.errorFields.password2)
}
}
} else {
const request = new Request(
`${BK_REGISTER_URL}`,
{
method:'POST',
headers: {
'Content-Type':'application/json',
'X-CSRFToken': csrfToken
},
credentials: 'include',
body: JSON.stringify( {
email: formData.get('login'),
password1: formData.get('password1'),
password2: formData.get('password2'),
}),
}
);
fetch(request).then(response => response.json())
.then(data => {
console.log('Success:', data);
setUserFieldError("")
setPassword1FieldError("")
setPassword2FieldError("")
setErrorMessage("")
if(isOK(data)){
setPopupMessage(data.message);
setPopupVisible(true);
} else {
if(data.errorMessage){
setErrorMessage(data.errorMessage);
}
if(data.errorFields){
setUserFieldError(data.errorFields.email)
setPassword1FieldError(data.errorFields.password1)
setPassword2FieldError(data.errorFields.password2)
}
}
})
.catch(error => {
console.error('Error fetching data:', error);
error = error.errorMessage;
console.log(error);
});
}
}
if (isLoading === true) {
return <Loader /> // Affichez le composant Loader
} else {
return <>
<div className="container max mx-auto p-4">
<div className="flex justify-center mb-4">
<Logo className="h-150 w-150" />
</div>
<h1 className="text-2xl font-bold text-center mb-4">Nouveau profil</h1>
<form className="max-w-md mx-auto" onSubmit={(e) => { e.preventDefault(); suscribe(new FormData(e.target)); }}>
<DjangoCSRFToken csrfToken={csrfToken} />
<InputTextIcon name="login" type="text" IconItem={User} label="Identifiant" placeholder="Identifiant" errorMsg={userFieldError} className="w-full" />
<InputTextIcon name="password1" type="password" IconItem={KeySquare} label="Mot de passe" placeholder="Mot de passe" errorMsg={password1FieldError} className="w-full" />
<InputTextIcon name="password2" type="password" IconItem={KeySquare} label="Confirmation mot de passe" placeholder="Confirmation mot de passe" errorMsg={password2FieldError} className="w-full" />
<p className="text-red-500">{errorMessage}</p>
<div className="form-group-submit mt-4">
<Button text="Enregistrer" className="w-full" primary type="submit" name="validate" />
</div>
</form>
<br/>
<div className='flex justify-center mt-2 max-w-md mx-auto'><Button text="Annuler" className="w-full" onClick={()=>{router.push(`${FR_USERS_LOGIN_URL}`)}} /></div>
</div>
<Popup
visible={popupVisible}
message={popupMessage}
onConfirm={() => {
setPopupVisible(false);
router.push(`${FR_USERS_LOGIN_URL}`);
}}
onCancel={() => setPopupVisible(false)}
/>
</>
}
}