From 895d0c7f6be5aaae00f7ca06b94e7facb9a36f83 Mon Sep 17 00:00:00 2001 From: Divlo Date: Sat, 21 Mar 2020 16:43:37 +0100 Subject: [PATCH] frontend: Loader + Refactoring --- .../components/FunctionCard/FunctionCard.css | 6 ++ .../components/FunctionCard/FunctionCard.js | 46 ++++++++----- frontend/components/Loader/Loader.css | 13 ++++ frontend/components/Loader/Loader.js | 11 ++++ frontend/hooks/useAPI.js | 31 +++++++++ frontend/pages/functions.js | 65 ++++++++----------- frontend/pages/index.js | 33 ++++++---- frontend/public/css/general.css | 3 + 8 files changed, 144 insertions(+), 64 deletions(-) create mode 100644 frontend/components/Loader/Loader.css create mode 100644 frontend/components/Loader/Loader.js create mode 100644 frontend/hooks/useAPI.js diff --git a/frontend/components/FunctionCard/FunctionCard.css b/frontend/components/FunctionCard/FunctionCard.css index 5196dbb..feddaa3 100644 --- a/frontend/components/FunctionCard/FunctionCard.css +++ b/frontend/components/FunctionCard/FunctionCard.css @@ -11,6 +11,12 @@ cursor: pointer; transition: all .3s; } +.FunctionCard__container { + height: 100%; + width: 100%; + display: flex; + flex-direction: column; +} .FunctionCard:hover { transform: translateY(-7px); } diff --git a/frontend/components/FunctionCard/FunctionCard.js b/frontend/components/FunctionCard/FunctionCard.js index b6f15c8..5e485aa 100644 --- a/frontend/components/FunctionCard/FunctionCard.js +++ b/frontend/components/FunctionCard/FunctionCard.js @@ -1,20 +1,36 @@ import Link from 'next/link'; +import { useState, Fragment } from 'react'; +import Loader from '../Loader/Loader'; import './FunctionCard.css'; -const FunctionCard = (props) => ( - -
-
- {props.title} -

{props.title}

-

{props.description}

-
-
-

{props.category.name}

-

{props.publicationDate}

-
-
- -); +const FunctionCard = (props) => { + + const [isLoading, setIsLoading] = useState(true); + + const handleLoad = () => { + setIsLoading(false); + } + + return ( + + +
+ {isLoading && } +
+
+ {props.title} +

{props.title}

+

{props.description}

+
+
+

{props.category.name}

+

{props.publicationDate}

+
+
+
+
+ + ); +} export default FunctionCard; \ No newline at end of file diff --git a/frontend/components/Loader/Loader.css b/frontend/components/Loader/Loader.css new file mode 100644 index 0000000..b7a8ac1 --- /dev/null +++ b/frontend/components/Loader/Loader.css @@ -0,0 +1,13 @@ +.Loader { + transform-origin: 50% 50%; animation: .9s linear 0s infinite normal forwards running Loader__spin; +} + +@keyframes Loader__spin { + 0% { + animation-timing-function: cubic-bezier(0.5856,0.0703,0.4143,0.9297); + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} \ No newline at end of file diff --git a/frontend/components/Loader/Loader.js b/frontend/components/Loader/Loader.js new file mode 100644 index 0000000..9a6e508 --- /dev/null +++ b/frontend/components/Loader/Loader.js @@ -0,0 +1,11 @@ +import './Loader.css'; + +const Loader = ({ width, height }) => ( + + + + + +); + +export default Loader; \ No newline at end of file diff --git a/frontend/hooks/useAPI.js b/frontend/hooks/useAPI.js new file mode 100644 index 0000000..02589a9 --- /dev/null +++ b/frontend/hooks/useAPI.js @@ -0,0 +1,31 @@ +import { useEffect, useState } from 'react'; +import api from '../config/api'; + +/** + * @param {String} url + * @param {*} defaultData + * @param {String} method + * @param {Object} options + */ +function useAPI(url, defaultData = [], method = "get", options = {}) { + + const [isLoading, setIsLoading] = useState(true); + const [data, setData] = useState(defaultData); + const [hasError, setHasError] = useState(false); + + useEffect(() => { + api[method](url, options) + .then((result) => { + setData(result.data); + setIsLoading(false); + }) + .catch((error) => { + setHasError(true); + console.error(error); + }); + }, []); + + return [isLoading, data, hasError]; +} + +export default useAPI; \ No newline at end of file diff --git a/frontend/pages/functions.js b/frontend/pages/functions.js index fb6c7b5..eed3271 100644 --- a/frontend/pages/functions.js +++ b/frontend/pages/functions.js @@ -1,60 +1,50 @@ import { Fragment, useState, useEffect } from 'react'; import HeadTag from '../components/HeadTag'; import FunctionCard from '../components/FunctionCard/FunctionCard'; +import Loader from '../components/Loader/Loader'; import '../public/css/pages/functions.css'; import { API_URL } from '../config/config'; import api from '../config/api'; +import useAPI from '../hooks/useAPI'; const Functions = () => { // State de recherche et de catégories - const [categories, setCategories] = useState([]); + const [, categories] = useAPI('/categories'); const [inputSearch, setInputSearch] = useState({ search: "", selectedCategory: "0" }); // State pour afficher les fonctions - const [functions, setFunctions] = useState([]); + const [functionsData, setFunctionsData] = useState({ hasMore: true, rows: [] }); const [isLoadingFunctions, setLoadingFunctions] = useState(true); const [pageFunctions, setPageFunctions] = useState(1); - const [hasMoreFunctions, sethasMoreFunctions] = useState(false); - // Récupère les catégories - useEffect(() => { - api.get('/categories') - .then((result) => { - setCategories(result.data); - }) - .catch((error) => console.error(error)); - }, []); - + const getFunctionsData = () => { + setLoadingFunctions(true); + return new Promise(async (next) => { + const result = await api.get(`/functions?page=${pageFunctions}&limit=10&categoryId=${inputSearch.selectedCategory}&search=${inputSearch.search}`); + setLoadingFunctions(false); + next(result.data); + }); + } + // Récupère les fonctions si la page change useEffect(() => { - api.get(`/functions?page=${pageFunctions}&limit=10&categoryId=${inputSearch.selectedCategory}&search=${inputSearch.search}`) - .then((result) => { - setLoadingFunctions(false); - sethasMoreFunctions(result.data.hasMore); - setFunctions([...functions, ...result.data.rows]); - }) - .catch((error) => console.error(error)); + getFunctionsData().then((data) => setFunctionsData({ + hasMore: data.hasMore, + rows: [...functionsData.rows, ...data.rows] + })); }, [pageFunctions]); // Récupère les fonctions si la catégorie/recherche change useEffect(() => { - api.get(`/functions?page=${pageFunctions}&limit=10&categoryId=${inputSearch.selectedCategory}&search=${inputSearch.search}`) - .then((result) => { - setLoadingFunctions(false); - sethasMoreFunctions(result.data.hasMore); - setFunctions(result.data.rows); - }) - .catch((error) => console.error(error)); + getFunctionsData().then((data) => setFunctionsData(data)); }, [inputSearch.selectedCategory, inputSearch.search]); const loadMore = () => { - setLoadingFunctions(true); setPageFunctions(pageFunctions + 1); } const handleChange = (event) => { - setLoadingFunctions(true); const inputSearchNew = { ...inputSearch }; inputSearchNew[event.target.name] = event.target.value; setInputSearch(inputSearchNew); @@ -72,7 +62,7 @@ const Functions = () => {

Fonctions

- +