Hotfix: Nom des dossiers + Ajout /functions/slug
This commit is contained in:
29
website/pages/404.js
Normal file
29
website/pages/404.js
Normal file
@ -0,0 +1,29 @@
|
||||
import { Fragment } from 'react';
|
||||
import Link from 'next/link';
|
||||
import HeadTag from '../components/HeadTag';
|
||||
import '../public/css/pages/404.css';
|
||||
|
||||
const Error404 = () => (
|
||||
<Fragment>
|
||||
<HeadTag
|
||||
title="Erreur 404"
|
||||
description="Cette page n'existe pas!"
|
||||
image="/images/error404.png"
|
||||
/>
|
||||
<div className="Error404__container">
|
||||
<h1>Erreur <span className="important">404</span></h1>
|
||||
<p className="text-center">
|
||||
Cette page n'existe pas! <Link href={"/"}><a>Revenir à la page d'accueil ?</a></Link>
|
||||
</p>
|
||||
</div>
|
||||
<style>
|
||||
{`
|
||||
#__next {
|
||||
padding-top: 0;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
export default Error404;
|
31
website/pages/_app.js
Normal file
31
website/pages/_app.js
Normal file
@ -0,0 +1,31 @@
|
||||
/* Libraries Imports */
|
||||
import { Fragment } from 'react';
|
||||
import Router from 'next/router'
|
||||
import NProgress from 'nprogress';
|
||||
|
||||
/* Components Imports */
|
||||
import Header from '../components/Header/Header';
|
||||
import Footer from '../components/Footer/Footer';
|
||||
|
||||
/* CSS Imports */
|
||||
import '../public/fonts/Montserrat/Montserrat.css';
|
||||
import '../public/css/normalize.css';
|
||||
import '../public/css/grid.css';
|
||||
import '../public/css/general.css';
|
||||
import '../public/css/nprogress.css';
|
||||
|
||||
Router.events.on('routeChangeStart', () => NProgress.start());
|
||||
Router.events.on('routeChangeComplete', () => NProgress.done());
|
||||
Router.events.on('routeChangeError', () => NProgress.done());
|
||||
|
||||
const App = ({ Component, pageProps }) => (
|
||||
<Fragment>
|
||||
<Header />
|
||||
<div className="content container-fluid">
|
||||
<Component {...pageProps} />
|
||||
</div>
|
||||
<Footer />
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
export default App;
|
28
website/pages/_document.js
Normal file
28
website/pages/_document.js
Normal file
@ -0,0 +1,28 @@
|
||||
import Document, { Html, Head, Main, NextScript } from "next/document";
|
||||
import Loader from '../components/Loader';
|
||||
|
||||
class MyDocument extends Document {
|
||||
static async getInitialProps(ctx) {
|
||||
const initialProps = await Document.getInitialProps(ctx);
|
||||
return { ...initialProps };
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Html lang="fr">
|
||||
<Head />
|
||||
<body>
|
||||
<div id="preloader">
|
||||
<Loader />
|
||||
</div>
|
||||
<div className="isLoading">
|
||||
<Main />
|
||||
</div>
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MyDocument;
|
26
website/pages/functions/[slug].js
Normal file
26
website/pages/functions/[slug].js
Normal file
@ -0,0 +1,26 @@
|
||||
import { Fragment } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import HeadTag from '../../components/HeadTag';
|
||||
import { API_URL } from '../../config/config';
|
||||
|
||||
const FunctionComponent = () => {
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<HeadTag
|
||||
title={router.query.slug}
|
||||
description={router.query.slug}
|
||||
image={`${API_URL}/images/functions/${router.query.slug}.png`}
|
||||
/>
|
||||
<div className="container text-center">
|
||||
<div className="row justify-content-center">
|
||||
<h1>{router.query.slug}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export default FunctionComponent;
|
100
website/pages/functions/index.js
Normal file
100
website/pages/functions/index.js
Normal file
@ -0,0 +1,100 @@
|
||||
import { Fragment, useState, useEffect, useRef, useCallback } from 'react';
|
||||
import HeadTag from '../../components/HeadTag';
|
||||
import FunctionCard from '../../components/FunctionCard/FunctionCard';
|
||||
import Loader from '../../components/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] = useAPI('/categories');
|
||||
const [inputSearch, setInputSearch] = useState({ search: "", selectedCategory: "0" });
|
||||
|
||||
// State pour afficher les fonctions
|
||||
const [functionsData, setFunctionsData] = useState({ hasMore: true, rows: [] });
|
||||
const [isLoadingFunctions, setLoadingFunctions] = useState(true);
|
||||
const [pageFunctions, setPageFunctions] = useState(1);
|
||||
|
||||
// Permet la pagination au scroll
|
||||
const observer = useRef();
|
||||
const lastFunctionCardRef = useCallback((node) => {
|
||||
if (isLoadingFunctions) return;
|
||||
if (observer.current) observer.current.disconnect();
|
||||
observer.current = new IntersectionObserver((entries) => {
|
||||
if (entries[0].isIntersecting && functionsData.hasMore) {
|
||||
setPageFunctions(pageFunctions + 1);
|
||||
}
|
||||
}, { threshold: 1 });
|
||||
if (node) observer.current.observe(node);
|
||||
}, [isLoadingFunctions, functionsData.hasMore]);
|
||||
|
||||
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(() => {
|
||||
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(() => {
|
||||
getFunctionsData().then((data) => setFunctionsData(data));
|
||||
}, [inputSearch.selectedCategory, inputSearch.search]);
|
||||
|
||||
const handleChange = (event) => {
|
||||
const inputSearchNew = { ...inputSearch };
|
||||
inputSearchNew[event.target.name] = event.target.value;
|
||||
setInputSearch(inputSearchNew);
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<HeadTag
|
||||
title="Fonctions"
|
||||
description="Liste des fonctions."
|
||||
image="/images/FunctionProject_icon_small.png"
|
||||
/>
|
||||
|
||||
<div className="container text-center">
|
||||
<div className="row justify-content-center">
|
||||
<h1 className="Functions__title">Fonctions</h1>
|
||||
</div>
|
||||
|
||||
<div className="Functions__search-container row justify-content-center">
|
||||
<select name="selectedCategory" value={inputSearch.selectedCategory} onChange={handleChange} className="Functions__select form-control">
|
||||
<option value="0">Toutes catégories</option>
|
||||
{categories.map((category) => (
|
||||
<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="form-control Functions__search-input" name="search" id="search" placeholder="🔎 Rechercher..."></input>
|
||||
</div>
|
||||
|
||||
<div className="row justify-content-center">
|
||||
{functionsData.rows.map((f, index) => {
|
||||
// Si c'est le dernier élément
|
||||
if (functionsData.rows.length === index + 1) {
|
||||
return <FunctionCard ref={lastFunctionCardRef} key={f.id} slug={f.slug} image={API_URL + f.image} title={f.title} description={f.description} category={f.categorie} publicationDate={new Date(f.createdAt).toLocaleDateString('fr-FR')} />;
|
||||
}
|
||||
return <FunctionCard key={f.id} slug={f.slug} image={API_URL + f.image} title={f.title} description={f.description} category={f.categorie} publicationDate={new Date(f.createdAt).toLocaleDateString('fr-FR')} />;
|
||||
})}
|
||||
</div>
|
||||
{isLoadingFunctions && <Loader />}
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export default Functions;
|
25
website/pages/index.js
Normal file
25
website/pages/index.js
Normal file
@ -0,0 +1,25 @@
|
||||
import { Fragment, useEffect } from 'react';
|
||||
import HeadTag from '../components/HeadTag';
|
||||
|
||||
const Home = () => {
|
||||
|
||||
useEffect(() => {
|
||||
console.log(
|
||||
'%c ⚙️ FunctionProject',
|
||||
'color: #ffd800; font-weight: bold; background-color: #181818;padding: 10px;border-radius: 10px;font-size: 20px'
|
||||
);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<HeadTag
|
||||
title="FunctionProject"
|
||||
description="FunctionProject est un projet créé par Divlo qui a pour but de rassembler plein de mini-programme permettant de faire plusieurs choses comme savoir la météo, générer un nombre aléatoire, etc."
|
||||
image="/images/FunctionProject_icon_small.png"
|
||||
/>
|
||||
<div>Home</div>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export default Home;
|
Reference in New Issue
Block a user