📦 NEW: frontend /users
This commit is contained in:
		| @@ -53,18 +53,16 @@ const FunctionsList = (props) => { | ||||
|         if (node) observer.current.observe(node); | ||||
|     }, [isLoadingFunctions, functionsData.hasMore]); | ||||
|  | ||||
|     const getFunctionsData = () => { | ||||
|     const getFunctionsData = async () => { | ||||
|         setLoadingFunctions(true); | ||||
|         return new Promise(async (next) => { | ||||
|             const URL = `${(props.isAdmin) ? "/admin/functions" : "/functions"}?page=${pageFunctions}&limit=10&categoryId=${inputSearch.selectedCategory}&search=${inputSearch.search}`; | ||||
|             const result = await api.get(URL, { | ||||
|                 headers: { | ||||
|                     ...(props.isAdmin && props.token != undefined) && { 'Authorization': props.token } | ||||
|                 } | ||||
|             }); | ||||
|             setLoadingFunctions(false); | ||||
|             next(result.data); | ||||
|         const URL = `${(props.isAdmin) ? "/admin/functions" : "/functions"}?page=${pageFunctions}&limit=10&categoryId=${inputSearch.selectedCategory}&search=${inputSearch.search}`; | ||||
|         const result = await api.get(URL, { | ||||
|             headers: { | ||||
|                 ...(props.isAdmin && props.token != undefined) && { 'Authorization': props.token } | ||||
|             } | ||||
|         }); | ||||
|         setLoadingFunctions(false); | ||||
|         return result.data; | ||||
|     } | ||||
|  | ||||
|     const handleChange = (event) => { | ||||
| @@ -86,7 +84,7 @@ const FunctionsList = (props) => { | ||||
|                         <option key={category.id} value={category.id} className="Functions__select-option" style={{ backgroundColor: category.color }}>{category.name}</option> | ||||
|                     ))} | ||||
|                 </select> | ||||
|                 <input value={inputSearch.search} onChange={handleChange} type="search" className="Functions__form-control Functions__search-input" name="search" id="search" placeholder="🔎 Rechercher..."></input> | ||||
|                 <input value={inputSearch.search} onChange={handleChange} type="search" className="Functions__form-control Functions__search-input" name="search" id="search" placeholder="🔎 Rechercher..." /> | ||||
|             </div> | ||||
|  | ||||
|             <div className="row justify-content-center"> | ||||
|   | ||||
| @@ -37,6 +37,7 @@ export default function Header() { | ||||
|                     <ul className={`navbar__list ${(isActive) ? "navbar__list-active" : ""}`}> | ||||
|                         <NavigationLink name="Accueil" path="/" /> | ||||
|                         <NavigationLink name="Fonctions" path="/functions" /> | ||||
|                         <NavigationLink name="Utilisateurs" path="/users" /> | ||||
|                         { | ||||
|                             (!isAuth) ?  | ||||
|                                 <Fragment> | ||||
|   | ||||
| @@ -52,7 +52,7 @@ const Profile = (props) => { | ||||
|                 .then(() => { | ||||
|                     setIsLoading(false); | ||||
|                     logoutUser(); | ||||
|                     redirect({}, '/login?isSuccessEdit=true'); | ||||
|                     redirect({}, '/users/login?isSuccessEdit=true'); | ||||
|                 }) | ||||
|                 .catch((error) => { | ||||
|                     setMessage(`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`); | ||||
|   | ||||
							
								
								
									
										109
									
								
								website/pages/users/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								website/pages/users/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | ||||
| import { Fragment, useState, useEffect, useRef, useCallback } from 'react'; | ||||
| import Link from 'next/link'; | ||||
| import HeadTag from '../../components/HeadTag'; | ||||
| import Loader from '../../components/Loader'; | ||||
| import api from '../../utils/api'; | ||||
| import '../../public/css/pages/users.css'; | ||||
| import { API_URL } from '../../utils/config/config'; | ||||
|  | ||||
| const Users = () => { | ||||
|  | ||||
|     let pageUsers = 1; | ||||
|      | ||||
|     const [inputSearch, setInputSearch]     = useState(""); | ||||
|     const [usersData, setUsersData]         = useState({ totalItems: 0, hasMore: true, rows: [] }); | ||||
|     const [isLoadingUsers, setLoadingUsers] = useState(true); | ||||
|  | ||||
|     // Récupère les users si la recherche change | ||||
|     useEffect(() => { | ||||
|         pageUsers = 1; | ||||
|         getUsersData().then((data) => setUsersData(data)); | ||||
|     }, [inputSearch]); | ||||
|  | ||||
|     const getUsersData = async () => { | ||||
|         setLoadingUsers(true); | ||||
|         const { data } = await api.get(`/users?page=${pageUsers}&limit=15&search=${inputSearch}`); | ||||
|         setLoadingUsers(false); | ||||
|         return data; | ||||
|     } | ||||
|  | ||||
|     const handleSearchChange = (event) => { | ||||
|         setInputSearch(event.target.value); | ||||
|     } | ||||
|  | ||||
|     // Permet la pagination au scroll | ||||
|     const observer = useRef(); | ||||
|     const lastUserCardRef = useCallback((node) => { | ||||
|         if (isLoadingUsers) return; | ||||
|         if (observer.current) observer.current.disconnect(); | ||||
|         observer.current = new IntersectionObserver((entries) => { | ||||
|             if (entries[0].isIntersecting && usersData.hasMore) { | ||||
|                 pageUsers += 1; | ||||
|                 getUsersData().then((data) => { | ||||
|                     setUsersData((oldData) => { | ||||
|                         return {  | ||||
|                             totalItems: data.totalItems, | ||||
|                             hasMore: data.hasMore,  | ||||
|                             rows: [...oldData.rows, ...data.rows]  | ||||
|                         } | ||||
|                     }); | ||||
|                 });  | ||||
|             } | ||||
|         }, { threshold: 1 }); | ||||
|         if (node) observer.current.observe(node); | ||||
|     }, [isLoadingUsers, usersData.hasMore]); | ||||
|  | ||||
|     return ( | ||||
|         <Fragment> | ||||
|             <HeadTag  | ||||
|                 title="Utilisateurs"  | ||||
|                 description="Liste des utilisateurs."  | ||||
|             /> | ||||
|  | ||||
|             <div className="container text-center"> | ||||
|                 <div className="row justify-content-center"> | ||||
|                     <div className="col-24"> | ||||
|                         <h1 style={{ marginBottom: 0, paddingTop: "20px" }}>Utilisateurs</h1> | ||||
|                         <p style={{ marginTop: '5px' }}>La liste des utilisateurs - Total de {usersData.totalItems} utilisateurs :</p> | ||||
|                     </div> | ||||
|                 </div> | ||||
|  | ||||
|                 <div className="Users__search-container row justify-content-center"> | ||||
|                     <input value={inputSearch} onChange={handleSearchChange} type="search" className="Users__form-control Users__search-input" name="search" id="search" placeholder="🔎 Rechercher..." /> | ||||
|                 </div> | ||||
|  | ||||
|                 <div className="row justify-content-center"> | ||||
|                     {usersData.rows.map((user, index) => { | ||||
|                         // Si c'est le dernier élément | ||||
|                         if (usersData.rows.length === index + 1) { | ||||
|                             return ( | ||||
|                                 <div ref={lastUserCardRef} key={user.id} className="UserCard col-sm-24 col-md-10 col-xl-7"> | ||||
|                                     <Link href={"/users/[name]"} as={`/users/${user.name}`}> | ||||
|                                         <div className="UserCard__container"> | ||||
|                                             <img className="UserCard__logo" src={API_URL + user.logo} alt={user.name} /> | ||||
|                                             <h2 className="UserCard__name">{user.name}</h2> | ||||
|                                         </div> | ||||
|                                     </Link> | ||||
|                                 </div> | ||||
|                             ); | ||||
|                         } | ||||
|                         return ( | ||||
|                             <div key={user.id} className="UserCard col-sm-24 col-md-10 col-xl-7"> | ||||
|                                 <Link href={"/users/[name]"} as={`/users/${user.name}`}> | ||||
|                                     <div className="UserCard__container"> | ||||
|                                             <img className="UserCard__logo" src={API_URL + user.logo} alt={user.name} /> | ||||
|                                             <h2 className="UserCard__name">{user.name}</h2> | ||||
|                                     </div> | ||||
|                                 </Link> | ||||
|                             </div> | ||||
|                         ); | ||||
|                     })} | ||||
|                 </div> | ||||
|  | ||||
|                 {isLoadingUsers && <Loader />} | ||||
|             </div> | ||||
|         </Fragment> | ||||
|     ); | ||||
| } | ||||
|  | ||||
| export default Users; | ||||
							
								
								
									
										65
									
								
								website/public/css/pages/users.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								website/public/css/pages/users.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| .Users__form-control { | ||||
|     display: block; | ||||
|     height: calc(1.5em + .75rem + 2px); | ||||
|     padding: .375rem .75rem; | ||||
|     font-size: 1rem; | ||||
|     font-weight: 400; | ||||
|     line-height: 1.5; | ||||
|     color: #495057; | ||||
|     background-color: #fff; | ||||
|     background-clip: padding-box; | ||||
|     border: 1px solid #ced4da; | ||||
|     border-radius: .5em; | ||||
| } | ||||
| .Users__search-container { | ||||
|     margin-bottom: 50px; | ||||
| } | ||||
| .Users__search-input { | ||||
|     width: 50%; | ||||
| } | ||||
|  | ||||
| .UserCard { | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     position: relative; | ||||
|     flex-direction: column; | ||||
|     word-wrap: break-word; | ||||
|     box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25); | ||||
|     border: 1px solid black; | ||||
|     border-radius: 1rem; | ||||
|     margin: 0 0 50px 0; | ||||
|     cursor: pointer; | ||||
|     transition: all .3s; | ||||
|     padding: 10px; | ||||
| } | ||||
| .UserCard__container { | ||||
|     height: 100%; | ||||
|     width: 100%; | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     align-items: center; | ||||
| } | ||||
| .UserCard:hover { | ||||
|     transform: translateY(-7px); | ||||
| } | ||||
| /* col-md */ | ||||
| @media (min-width: 768px) { | ||||
|     .UserCard { | ||||
|         margin: 0 30px 50px 30px; | ||||
|     }  | ||||
| } | ||||
| /* col-xl */ | ||||
| @media (min-width: 1200px) { | ||||
|     .UserCard { | ||||
|         margin: 0 20px 50px 20px; | ||||
|     }    | ||||
| } | ||||
| .UserCard__logo { | ||||
|     width: 150px; | ||||
|     height: 150px; | ||||
|     border-radius: 50%; | ||||
|     object-fit: cover; | ||||
| } | ||||
| .UserCard__name { | ||||
|     margin: 30px 0 10px 0; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user