feat: Ajout d'une fonction de logout

This commit is contained in:
N3WT DE COMPET
2025-02-17 18:18:52 +01:00
parent 8ea68bbad0
commit c2bba1abbf
7 changed files with 239 additions and 163 deletions

View File

@ -15,6 +15,7 @@ import {
} from 'lucide-react'; } from 'lucide-react';
import DropdownMenu from '@/components/DropdownMenu'; import DropdownMenu from '@/components/DropdownMenu';
import Logo from '@/components/Logo'; import Logo from '@/components/Logo';
import Popup from '@/components/Popup';
import { import {
FE_ADMIN_HOME_URL, FE_ADMIN_HOME_URL,
FE_ADMIN_SUBSCRIPTIONS_URL, FE_ADMIN_SUBSCRIPTIONS_URL,
@ -45,6 +46,7 @@ export default function Layout({
const [establishment, setEstablishment] = useState(null); const [establishment, setEstablishment] = useState(null);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [isPopupVisible, setIsPopupVisible] = useState(false);
const pathname = usePathname(); const pathname = usePathname();
const currentPage = pathname.split('/').pop(); const currentPage = pathname.split('/').pop();
@ -54,11 +56,19 @@ export default function Layout({
const softwareName = "N3WT School"; const softwareName = "N3WT School";
const softwareVersion = `v${process.env.NEXT_PUBLIC_APP_VERSION}`; const softwareVersion = `v${process.env.NEXT_PUBLIC_APP_VERSION}`;
const handleDisconnect = () => {
setIsPopupVisible(true);
};
const confirmDisconnect = () => {
setIsPopupVisible(false);
disconnect();
};
const dropdownItems = [ const dropdownItems = [
{ {
label: 'Déconnexion', label: 'Déconnexion',
onClick: disconnect, onClick: handleDisconnect,
icon: LogOut, icon: LogOut,
}, },
]; ];
@ -74,8 +84,6 @@ export default function Layout({
}, []); }, []);
return ( return (
<>
<SessionProvider> <SessionProvider>
<ProtectedRoute> <ProtectedRoute>
{!isLoading && ( {!isLoading && (
@ -110,10 +118,14 @@ export default function Layout({
</div> </div>
</div> </div>
)} )}
<Popup
visible={isPopupVisible}
message="Êtes-vous sûr(e) de vouloir vous déconnecter ?"
onConfirm={confirmDisconnect}
onCancel={() => setIsPopupVisible(false)}
/>
</ProtectedRoute> </ProtectedRoute>
</SessionProvider> </SessionProvider>
</>
); );
} }

View File

@ -44,6 +44,7 @@ import {
import DjangoCSRFToken from '@/components/DjangoCSRFToken' import DjangoCSRFToken from '@/components/DjangoCSRFToken'
import { useCsrfToken } from '@/context/CsrfContext'; import { useCsrfToken } from '@/context/CsrfContext';
import { fetchRegistrationFileGroups } from '@/app/lib/registerFileGroupAction'; import { fetchRegistrationFileGroups } from '@/app/lib/registerFileGroupAction';
import { ESTABLISHMENT_ID } from '@/utils/Url';
const useFakeData = process.env.NEXT_PUBLIC_USE_FAKE_DATA === 'true'; const useFakeData = process.env.NEXT_PUBLIC_USE_FAKE_DATA === 'true';
@ -371,7 +372,8 @@ useEffect(()=>{
idGuardians: selectedGuardiansIds, idGuardians: selectedGuardiansIds,
fees: allFeesIds, fees: allFeesIds,
discounts: allDiscountsds, discounts: allDiscountsds,
fileGroup: selectedFileGroup fileGroup: selectedFileGroup,
establishment: ESTABLISHMENT_ID
}; };
createRegisterForm(data, csrfToken) createRegisterForm(data, csrfToken)
@ -413,7 +415,8 @@ useEffect(()=>{
sibling: [] sibling: []
}, },
fees: allFeesIds, fees: allFeesIds,
discounts: allDiscountsds discounts: allDiscountsds,
establishment: ESTABLISHMENT_ID
}; };
createRegisterForm(data, csrfToken) createRegisterForm(data, csrfToken)

View File

@ -9,6 +9,9 @@ import { FE_PARENTS_HOME_URL,FE_PARENTS_MESSAGERIE_URL,FE_PARENTS_SETTINGS_URL
import useLocalStorage from '@/hooks/useLocalStorage'; import useLocalStorage from '@/hooks/useLocalStorage';
import { fetchMessages } from '@/app/lib/messagerieAction'; import { fetchMessages } from '@/app/lib/messagerieAction';
import ProtectedRoute from '@/components/ProtectedRoute'; import ProtectedRoute from '@/components/ProtectedRoute';
import { SessionProvider } from 'next-auth/react';
import { disconnect } from '@/app/lib/authAction';
import Popup from '@/components/Popup';
export default function Layout({ export default function Layout({
children, children,
@ -18,6 +21,16 @@ export default function Layout({
const [messages, setMessages] = useState([]); const [messages, setMessages] = useState([]);
const [userId, setUserId] = useLocalStorage("userId", '') ; const [userId, setUserId] = useLocalStorage("userId", '') ;
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [isPopupVisible, setIsPopupVisible] = useState(false);
const handleDisconnect = () => {
setIsPopupVisible(true);
};
const confirmDisconnect = () => {
setIsPopupVisible(false);
disconnect();
};
useEffect(() => { useEffect(() => {
setIsLoading(true); setIsLoading(true);
@ -42,6 +55,7 @@ export default function Layout({
} }
return ( return (
<SessionProvider>
<ProtectedRoute> <ProtectedRoute>
<div className="flex flex-col min-h-screen bg-gray-50"> <div className="flex flex-col min-h-screen bg-gray-50">
{/* Entête */} {/* Entête */}
@ -75,7 +89,7 @@ export default function Layout({
<DropdownMenu <DropdownMenu
buttonContent={<User />} buttonContent={<User />}
items={[ items={[
{ label: 'Se déconnecter', icon: LogOut, onClick: () => {} }, { label: 'Se déconnecter', icon: LogOut, onClick: handleDisconnect },
{ label: 'Settings', icon: Settings , onClick: () => { router.push(FE_PARENTS_SETTINGS_URL); } } { label: 'Settings', icon: Settings , onClick: () => { router.push(FE_PARENTS_SETTINGS_URL); } }
]} ]}
buttonClassName="p-2 rounded-full hover:bg-gray-200" buttonClassName="p-2 rounded-full hover:bg-gray-200"
@ -90,7 +104,14 @@ export default function Layout({
{children} {children}
</div> </div>
</div> </div>
<Popup
visible={isPopupVisible}
message="Êtes-vous sûr(e) de vouloir vous déconnecter ?"
onConfirm={confirmDisconnect}
onCancel={() => setIsPopupVisible(false)}
/>
</ProtectedRoute> </ProtectedRoute>
</SessionProvider>
); );
} }

View File

@ -36,7 +36,7 @@ export default function Page() {
return data.errorMessage === "" return data.errorMessage === ""
} }
async function handleFormLogin(formData) { /*async function handleFormLogin(formData) {
setIsLoading(true); setIsLoading(true);
try { try {
@ -76,6 +76,51 @@ export default function Page() {
setIsLoading(false); setIsLoading(false);
setErrorMessage('An error occurred during sign in.'); setErrorMessage('An error occurred during sign in.');
} }
}*/
function handleFormLogin(formData) {
setIsLoading(true);
signIn('credentials', {
redirect: false,
email: formData.get('login'),
password: formData.get('password'),
}).then(result => {
console.log('Sign In Result', result);
setIsLoading(false);
if (result.error) {
setErrorMessage(result.error);
} else {
getSession().then(session => {
if (!session || !session.user) {
throw new Error('Session not found');
}
const user = session.user;
console.log('User Session:', user);
localStorage.setItem('userId', user.id); // Stocker l'identifiant de l'utilisateur
if (user.droit === 0) {
// Vue ECOLE
} else if (user.droit === 1) {
// Vue ADMIN
router.push(FE_ADMIN_SUBSCRIPTIONS_URL);
} else if (user.droit === 2) {
// Vue PARENT
router.push(FE_PARENTS_HOME_URL);
} else {
// Cas anormal
}
}).catch(error => {
console.error('Error during session retrieval:', error);
setIsLoading(false);
setErrorMessage('An error occurred during session retrieval.');
});
}
}).catch(error => {
console.error('Error during sign in:', error);
setIsLoading(false);
setErrorMessage('An error occurred during sign in.');
});
} }
if (isLoading === true) { if (isLoading === true) {

View File

@ -1,104 +1,103 @@
import { signOut } from 'next-auth/react';
import { import {
BE_AUTH_LOGIN_URL, BE_AUTH_LOGIN_URL,
BE_AUTH_REGISTER_URL, BE_AUTH_REGISTER_URL,
BE_AUTH_PROFILES_URL, BE_AUTH_PROFILES_URL,
BE_AUTH_RESET_PASSWORD_URL, BE_AUTH_RESET_PASSWORD_URL,
BE_AUTH_NEW_PASSWORD_URL, BE_AUTH_NEW_PASSWORD_URL,
FE_USERS_LOGIN_URL , FE_USERS_LOGIN_URL,
} from '@/utils/Url'; } from '@/utils/Url';
import {mockUser} from "@/data/mockUsersData";
const useFakeData = process.env.NEXT_PUBLIC_USE_FAKE_DATA === 'true';
const requestResponseHandler = async (response) => { const requestResponseHandler = async (response) => {
const body = await response.json(); const body = await response.json();
if (response.ok) { if (response.ok) {
return body; return body;
} }
// Throw an error with the JSON body containing the form errors
const error = new Error('Form submission error'); const error = new Error('Form submission error');
error.details = body; error.details = body;
throw error; throw error;
} };
export const login = (data, csrfToken) => { /*export const login = (data, csrfToken) => {
console.log('data', data);
const request = new Request( const request = new Request(
`${BE_AUTH_LOGIN_URL}`, `${BE_AUTH_LOGIN_URL}`,
{ {
method:'POST', method: 'POST',
headers: { headers: {
'Content-Type':'application/json', 'Content-Type': 'application/json',
'X-CSRFToken': csrfToken 'X-CSRFToken': csrfToken
}, },
body: JSON.stringify(data), body: JSON.stringify(data),
credentials: 'include', credentials: 'include',
} }
); );
return fetch(request).then(requestResponseHandler) return fetch(request).then(requestResponseHandler);
} };*/
export const login = (data, csrfToken) => {
const request = new Promise((resolve, reject) => {
signIn('credentials', {
redirect: false,
email: data.email,
password: data.password,
}).then(result => {
if (result.error) {
reject(new Error(result.error));
} else {
resolve(result);
}
}).catch(reject);
});
return request.then(requestResponseHandler);
};
/** /**
* Disconnects the user after confirming the action. * Disconnects the user after confirming the action.
* If `NEXT_PUBLIC_USE_FAKE_DATA` environment variable is set to 'true', it will log a fake disconnect and redirect to the login URL. * If `NEXT_PUBLIC_USE_FAKE_DATA` environment variable is set to 'true', it will log a fake disconnect and redirect to the login URL.
* Otherwise, it will send a PUT request to the backend to update the user profile and then redirect to the login URL. * Otherwise, it will call `signOut` from NextAuth.js to handle the logout.
* *
* @function * @function
* @name disconnect * @name disconnect
* @returns {void} * @returns {void}
*/ */
export const disconnect = () => { export const disconnect = () => {
if (confirm("\nÊtes-vous sûr(e) de vouloir vous déconnecter ?")) { signOut({ callbackUrl: FE_USERS_LOGIN_URL });
};
if (useFakeData) { export const createProfile = (data, csrfToken) => {
console.log('Fake disconnect:', mockUser); const request = new Request(
router.push(`${FE_USERS_LOGIN_URL}`);
} else {
console.log('Fake disconnect:', mockUser);
router.push(`${FE_USERS_LOGIN_URL}`);
}
}
};
export const createProfile = (data,csrfToken) => {
const request = new Request(
`${BE_AUTH_PROFILES_URL}`, `${BE_AUTH_PROFILES_URL}`,
{ {
method:'POST', method: 'POST',
headers: { headers: {
'Content-Type':'application/json', 'Content-Type': 'application/json',
'X-CSRFToken': csrfToken 'X-CSRFToken': csrfToken
}, },
credentials: 'include', credentials: 'include',
body: JSON.stringify(data), body: JSON.stringify(data),
} }
); );
return fetch(request).then(requestResponseHandler) return fetch(request).then(requestResponseHandler);
} };
export const updateProfile = (id, data, csrfToken) => { export const updateProfile = (id, data, csrfToken) => {
const request = new Request( const request = new Request(
`${BE_AUTH_PROFILES_URL}/${id}`, `${BE_AUTH_PROFILES_URL}/${id}`,
{ {
method:'PUT', method: 'PUT',
headers: { headers: {
'Content-Type':'application/json', 'Content-Type': 'application/json',
'X-CSRFToken': csrfToken 'X-CSRFToken': csrfToken
}, },
credentials: 'include', credentials: 'include',
body: JSON.stringify(data), body: JSON.stringify(data),
} }
); );
return fetch(request).then(requestResponseHandler) return fetch(request).then(requestResponseHandler);
} };
export const sendNewPassword = (data, csrfToken) => { export const sendNewPassword = (data, csrfToken) => {
const request = new Request( const request = new Request(
`${BE_AUTH_NEW_PASSWORD_URL}`, `${BE_AUTH_NEW_PASSWORD_URL}`,
{ {
@ -111,46 +110,46 @@ export const sendNewPassword = (data, csrfToken) => {
body: JSON.stringify(data), body: JSON.stringify(data),
} }
); );
return fetch(request).then(requestResponseHandler) return fetch(request).then(requestResponseHandler);
} };
export const subscribe = (data,csrfToken) =>{ export const subscribe = (data, csrfToken) => {
const request = new Request( const request = new Request(
`${BE_AUTH_REGISTER_URL}`, `${BE_AUTH_REGISTER_URL}`,
{ {
method:'POST', method: 'POST',
headers: { headers: {
'Content-Type':'application/json', 'Content-Type': 'application/json',
'X-CSRFToken': csrfToken
},
credentials: 'include',
body: JSON.stringify( data),
}
);
return fetch(request).then(requestResponseHandler)
}
export const resetPassword = (uuid, data, csrfToken) => {
const request = new Request(
`${BE_AUTH_RESET_PASSWORD_URL}/${uuid}`,
{
method:'POST',
headers: {
'Content-Type':'application/json',
'X-CSRFToken': csrfToken 'X-CSRFToken': csrfToken
}, },
credentials: 'include', credentials: 'include',
body: JSON.stringify(data), body: JSON.stringify(data),
} }
); );
return fetch(request).then(requestResponseHandler) return fetch(request).then(requestResponseHandler);
} };
export const resetPassword = (uuid, data, csrfToken) => {
const request = new Request(
`${BE_AUTH_RESET_PASSWORD_URL}/${uuid}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken
},
credentials: 'include',
body: JSON.stringify(data),
}
);
return fetch(request).then(requestResponseHandler);
};
export const getResetPassword = (uuid) => { export const getResetPassword = (uuid) => {
const url= `${BE_AUTH_RESET_PASSWORD_URL}/${uuid}`; const url = `${BE_AUTH_RESET_PASSWORD_URL}/${uuid}`;
return fetch(url, { return fetch(url, {
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
}).then(requestResponseHandler) }).then(requestResponseHandler);
} };

View File

@ -40,7 +40,12 @@ const options = {
}) })
], ],
session: { session: {
jwt: true jwt: true,
maxAge: 24 * 60 * 60, // 1 day in seconds
updateAge: 24 * 60 * 60 // Update session every day
},
jwt: {
maxAge: 24 * 60 * 60 // 1 day in seconds
}, },
callbacks: { callbacks: {
async jwt({ token, user }) { async jwt({ token, user }) {

View File

@ -1,9 +0,0 @@
import { signOut } from 'next-auth/react';
export default function SignOut() {
return (
<button onClick={() => signOut({ callbackUrl: '/' })}>
Sign out
</button>
);
}