📦 NEW: frontend /users
This commit is contained in:
parent
db3f940da5
commit
516be91519
@ -53,9 +53,8 @@ const FunctionsList = (props) => {
|
|||||||
if (node) observer.current.observe(node);
|
if (node) observer.current.observe(node);
|
||||||
}, [isLoadingFunctions, functionsData.hasMore]);
|
}, [isLoadingFunctions, functionsData.hasMore]);
|
||||||
|
|
||||||
const getFunctionsData = () => {
|
const getFunctionsData = async () => {
|
||||||
setLoadingFunctions(true);
|
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 URL = `${(props.isAdmin) ? "/admin/functions" : "/functions"}?page=${pageFunctions}&limit=10&categoryId=${inputSearch.selectedCategory}&search=${inputSearch.search}`;
|
||||||
const result = await api.get(URL, {
|
const result = await api.get(URL, {
|
||||||
headers: {
|
headers: {
|
||||||
@ -63,8 +62,7 @@ const FunctionsList = (props) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
setLoadingFunctions(false);
|
setLoadingFunctions(false);
|
||||||
next(result.data);
|
return result.data;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleChange = (event) => {
|
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>
|
<option key={category.id} value={category.id} className="Functions__select-option" style={{ backgroundColor: category.color }}>{category.name}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</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>
|
||||||
|
|
||||||
<div className="row justify-content-center">
|
<div className="row justify-content-center">
|
||||||
|
@ -37,6 +37,7 @@ export default function Header() {
|
|||||||
<ul className={`navbar__list ${(isActive) ? "navbar__list-active" : ""}`}>
|
<ul className={`navbar__list ${(isActive) ? "navbar__list-active" : ""}`}>
|
||||||
<NavigationLink name="Accueil" path="/" />
|
<NavigationLink name="Accueil" path="/" />
|
||||||
<NavigationLink name="Fonctions" path="/functions" />
|
<NavigationLink name="Fonctions" path="/functions" />
|
||||||
|
<NavigationLink name="Utilisateurs" path="/users" />
|
||||||
{
|
{
|
||||||
(!isAuth) ?
|
(!isAuth) ?
|
||||||
<Fragment>
|
<Fragment>
|
||||||
|
@ -52,7 +52,7 @@ const Profile = (props) => {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
logoutUser();
|
logoutUser();
|
||||||
redirect({}, '/login?isSuccessEdit=true');
|
redirect({}, '/users/login?isSuccessEdit=true');
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
setMessage(`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`);
|
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