feat: Add blogs components

feature/blog
Näser 3 years ago
parent 140b61e1d7
commit 7f8e5dd903

@ -23,7 +23,9 @@
}
],
"rules": {
"@next/next/no-html-link-for-pages": "off"
"@next/next/no-html-link-for-pages": "off",
"@typescript-eslint/ban-ts-comment": "off",
"react/no-unescaped-entities": "off"
},
"env": {
"jest": true

@ -0,0 +1,7 @@
/*
* Replace this with your own classes
*
* e.g.
* .container {
* }
*/

@ -0,0 +1,10 @@
import { render } from '@testing-library/react';
import BlogPagination from './blog-pagination';
describe('BlogPagination', () => {
it('should render successfully', () => {
const { baseElement } = render(<BlogPagination />);
expect(baseElement).toBeTruthy();
});
});

@ -0,0 +1,74 @@
import styles from './blog-pagination.module.scss';
import {useRouter} from "next/router";
/* eslint-disable-next-line */
export interface BlogPaginationProps {
paginator: any;
}
export function BlogPagination({paginator}: BlogPaginationProps) {
const router = useRouter();
const {total, pageCount, page} = paginator.pagination;
const isOnlyOne = pageCount > 0 && pageCount === 1;
const isEmpty = total === 0;
const isFirst = page === 1;
const isLast = page === pageCount;
const goPrevious = async () => {
await router.push(`${window.location.origin}${window.location.pathname}?page=${page - 1}`);
}
const goNext = async () => {
await router.push(`${window.location.origin}${window.location.pathname}?page=${page + 1}`);
}
return (
<div className={styles['container']}>
<div className="flex flex-col items-center">
{isOnlyOne && !isEmpty && (
<span className="text-sm text-gray-700 dark:text-gray-400">{total} article(s)</span>)}
{!isOnlyOne && !isEmpty && (<span className="text-sm text-gray-700 dark:text-gray-400">
Articles <span className="font-semibold text-gray-900 dark:text-white">{page}</span> à <span
className="font-semibold text-gray-900 dark:text-white">{pageCount}</span> sur un total de <span
className="font-semibold text-gray-900 dark:text-white">{total}</span>
</span>)}
<div className="inline-flex mt-2 xs:mt-0">
<button
disabled={isOnlyOne || isFirst || isEmpty}
onClick={goPrevious}
className="
inline-flex items-center px-4 py-2 mr-2 text-sm font-medium rounded
text-white bg-gray-800
hover:bg-gray-900
disabled:bg-gray-500 disabled:hover:bg-gray-500
dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white">
<svg aria-hidden="true" className="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg">
<path fillRule="evenodd"
d="M7.707 14.707a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 1.414L5.414 9H17a1 1 0 110 2H5.414l2.293 2.293a1 1 0 010 1.414z"
clipRule="evenodd"></path>
</svg>
Prev
</button>
<button
disabled={isOnlyOne || isLast || isEmpty}
onClick={goNext}
className="inline-flex items-center px-4 py-2 ml-2 text-sm font-medium rounded
text-white bg-gray-800
hover:bg-gray-900
disabled:bg-gray-500 disabled:hover:bg-gray-500
dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white">
Next
<svg aria-hidden="true" className="w-5 h-5 ml-2" fill="currentColor" viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg">
<path fillRule="evenodd"
d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z"
clipRule="evenodd"></path>
</svg>
</button>
</div>
</div>
</div>
);
}
export default BlogPagination;

@ -0,0 +1,7 @@
/*
* Replace this with your own classes
*
* e.g.
* .container {
* }
*/

@ -0,0 +1,10 @@
import { render } from '@testing-library/react';
import BlogSearch from './blog-search';
describe('BlogSearch', () => {
it('should render successfully', () => {
const { baseElement } = render(<BlogSearch />);
expect(baseElement).toBeTruthy();
});
});

@ -0,0 +1,34 @@
import styles from './blog-search.module.scss';
/* eslint-disable-next-line */
export interface BlogSearchProps {
}
export function BlogSearch(props: BlogSearchProps) {
return (
<div className={styles['container'] + " bg-white rounded-lg mx-2 lg:mx-0 mt-5 lg:mt-0 p-5 mb-5 shadow-sm"}>
<form>
<label htmlFor="default-search"
className="mb-2 text-sm font-medium text-gray-900 sr-only dark:text-white">Search</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
<svg aria-hidden="true" className="w-5 h-5 text-gray-500 dark:text-gray-400" fill="none"
stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2"
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
</div>
<input type="search" id="default-search"
className="block w-full p-4 pl-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Rechercher un article ..." required />
<button type="submit"
className="text-white absolute right-2.5 bottom-2.5 bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-4 py-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">Search
</button>
</div>
</form>
</div>
);
}
export default BlogSearch;

@ -0,0 +1,7 @@
/*
* Replace this with your own classes
*
* e.g.
* .container {
* }
*/

@ -0,0 +1,10 @@
import { render } from '@testing-library/react';
import CardBlogDetails from './card-blog-details';
describe('CardBlogDetails', () => {
it('should render successfully', () => {
const { baseElement } = render(<CardBlogDetails />);
expect(baseElement).toBeTruthy();
});
});

@ -0,0 +1,111 @@
import delve from 'dlv';
import NextImage from "next/image";
import {
FacebookIcon,
FacebookMessengerIcon,
FacebookMessengerShareButton,
FacebookShareButton,
LinkedinIcon,
LinkedinShareButton,
TwitterIcon,
TwitterShareButton,
WhatsappIcon,
WhatsappShareButton
} from "react-share";
import {environment} from "../../environments/environment";
import styles from './card-blog-details.module.scss';
/* eslint-disable-next-line */
export interface CardBlogDetailsProps {
item: any;
}
export function CardBlogDetails({item}: CardBlogDetailsProps) {
const {alternativeText, width, height} = item.image.data.attributes;
const {slug} = item.category.data.attributes;
const {title} = item;
const shareUrl = `${environment.appUrl}/blog/${slug}/${item.slug}`;
console.log(shareUrl);
return (
<div className={styles['container'] + " bg-white rounded-l p-8 overflow-hidden shadow-sm"}>
<header className="md:flex flex-col">
<h2 className="text-3xl mb-5 text-bold">
{delve(item, 'title', 'N/A')}
</h2>
<section className="flex items-center justify-between">
<div className="flex">
<a href="#" className="flex items-center">
<img
src="https://images.unsplash.com/photo-1492562080023-ab3db95bfbce?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=crop&amp;w=731&amp;q=80"
alt="avatar" className="mr-4 w-10 h-10 object-cover rounded-full hidden sm:block"/>
<h3 className="text-gray-700 text-normal font-bold hover:underline text-slate-400">Par Alex John</h3>
</a>
</div>
<div className="flex flex-col">
<a href={'/blog/' + delve(item, 'category.data.attributes.slug', '')}
className="px-2 py-1 text-sm bg-gray-600 text-gray-100 font-bold rounded hover:bg-gray-500">
{delve(item, 'category.data.attributes.name', 'N/A')}
</a>
</div>
</section>
</header>
<hr className="border-gray-100 my-5 sm:mx-auto dark:border-gray-700 container mx-auto"/>
<main>
<section>
<div className="whitespace-pre-wrap text-lg font-bold text-slate-500"
dangerouslySetInnerHTML={{__html: delve(item, 'description', 'N/A')}}/>
<NextImage
layout="responsive"
width={width}
height={height}
objectFit="contain"
className="rounded-lg my-5"
src={environment.strapiUrl + delve(item, 'image.data.attributes.url', '')}
alt={delve(item, 'title', 'N/A')}/>
</section>
<section className="whitespace-pre-wrap text-lg text-slate-400"
dangerouslySetInnerHTML={{__html: delve(item, 'content', 'N/A')}}/>
<hr className="border-gray-100 my-5 sm:mx-auto dark:border-gray-700 container mx-auto"/>
<section className="flex justify-between">
<div>
<h4 className="font-bold text-lg text-gray-400 mb-2">Partager cet article</h4>
<div className="grid grid-cols-5 gap-2">
<FacebookShareButton url={shareUrl}>
<FacebookIcon size={32} round/>
</FacebookShareButton>
<FacebookMessengerShareButton
url={shareUrl}
appId="521270401588372"
className="Demo__some-network__share-button"
>
<FacebookMessengerIcon size={32} round/>
</FacebookMessengerShareButton>
<TwitterShareButton
url={shareUrl}
title={title}
className="Demo__some-network__share-button"
>
<TwitterIcon size={32} round/>
</TwitterShareButton>
<WhatsappShareButton
url={shareUrl}
title={title}
separator=":: "
className="Demo__some-network__share-button"
>
<WhatsappIcon size={32} round/>
</WhatsappShareButton>
<LinkedinShareButton url={shareUrl} className="Demo__some-network__share-button">
<LinkedinIcon size={32} round/>
</LinkedinShareButton>
</div>
</div>
</section>
</main>
</div>
);
}
export default CardBlogDetails;

@ -0,0 +1,7 @@
/*
* Replace this with your own classes
*
* e.g.
* .container {
* }
*/

@ -0,0 +1,10 @@
import { render } from '@testing-library/react';
import CardBlog from './card-blog';
describe('CardBlog', () => {
it('should render successfully', () => {
const { baseElement } = render(<CardBlog />);
expect(baseElement).toBeTruthy();
});
});

@ -0,0 +1,57 @@
import delve from 'dlv';
import { format } from 'date-fns';
import {environment} from '../../environments/environment';
import styles from './card-blog.module.scss';
/* eslint-disable-next-line */
export interface CardBlogProps {
item: object;
}
export function CardBlog({item}: CardBlogProps) {
return (
<div className={styles['container'] + " bg-white rounded-l overflow-hidden shadow-sm"}>
<div className="md:flex">
<div className="md:shrink-0">
<img className="h-48 w-full object-cover md:h-full md:w-48"
src={environment.strapiUrl + delve(item, 'attributes.image.data.attributes.formats.medium.url', '')}
alt="Modern building architecture"/>
</div>
<div className="p-8 w-full">
<div className="flex justify-between items-center">
<span
className="font-light text-gray-600 text-sm">{format(new Date(delve(item, 'attributes.publishedAt', 'Jun 1, 2020')),'dd/MM/yyyy')}</span>
<a href={'/blog/' + delve(item, 'attributes.category.data.attributes.slug', '')}
className="px-2 py-1 text-sm bg-gray-600 text-gray-100 font-bold rounded hover:bg-gray-500">
{delve(item, 'attributes.category.data.attributes.name', 'N/A')}
</a>
</div>
<div className="mt-2">
<a
href={'/blog/' + delve(item, 'attributes.category.data.attributes.slug', '') + '/' + delve(item, 'attributes.slug', '')}
className="text-xl text-gray-700 font-bold hover:underline">
{delve(item, 'attributes.title', 'N/A')}
</a>
<p className="mt-2 font-light text-sm text-gray-600">{delve(item, 'attributes.description', 'N/A')}</p>
</div>
<div className="flex justify-between items-center mt-4">
<a href="#" className="text-blue-500 text-sm hover:underline">Lire l'article</a>
<div>
<a href="#" className="flex items-center">
<img
src="https://images.unsplash.com/photo-1492562080023-ab3db95bfbce?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=crop&amp;w=731&amp;q=80"
alt="avatar" className="mx-4 w-10 h-10 object-cover rounded-full hidden sm:block"/>
<h3 className="text-gray-700 text-normal font-bold hover:underline">Alex John</h3>
</a>
</div>
</div>
</div>
</div>
</div>
);
}
export default CardBlog;

@ -0,0 +1,7 @@
/*
* Replace this with your own classes
*
* e.g.
* .container {
* }
*/

@ -0,0 +1,10 @@
import { render } from '@testing-library/react';
import Categories from './categories';
describe('Categories', () => {
it('should render successfully', () => {
const { baseElement } = render(<Categories />);
expect(baseElement).toBeTruthy();
});
});

@ -0,0 +1,43 @@
import delve from 'dlv';
import {environment} from "../../environments/environment";
import styles from './categories.module.scss';
import Link from "next/link";
/* eslint-disable-next-line */
export interface CategoriesProps {
items: object[];
}
// <!-- pb-0 sm:pt-4 -->
export function Categories({items = []}: CategoriesProps) {
return (
<div
className={styles['container'] + " bg-white rounded-lg mx-2 lg:mx-0 mt-5 lg:mt-0 p-5 mb-5 shadow-sm"}>
<div className="flex items-center justify-between mb-4">
<h5 className="text-xl font-bold leading-none text-gray-900 dark:text-white">Catégories</h5>
</div>
<div className="flow-root">
<ul role="list" className="divide-y divide-gray-200 dark:divide-gray-700">
{items.map((item: any, index: number) => (<li key={'categories-' + item.slug + index} className="py-3 sm:py-4">
<div className="flex items-center space-x-4">
<div className="flex-shrink-0">
<img className="w-8 h-8 rounded-full"
src={environment.strapiUrl + delve(item, 'attributes.image.data.attributes.formats.thumbnail.url', '')}
alt="Neil image"/>
</div>
<div className="flex-1 min-w-0">
<h4><Link href={"/blog/" + item.attributes.slug}>{delve(item, 'attributes.name', 'N/A')}</Link></h4>
</div>
</div>
</li>))
}
</ul>
</div>
</div>
);
}
export default Categories;

@ -178,13 +178,13 @@ export function Header({items = []}) {
<div className="flex mx-auto">
<main className="w-full">
<section
className="flex bg-gray-50 dark:bg-gray-900 flex-wrap items-center justify-between w-full md:w-auto p-2 md:p-4"
className="bg-white dark:bg-gray-900 w-full md:w-auto p-2 md:p-4"
id="navbar-default">
<div className="container max-w-screen-xl flex flex-wrap items-center justify-between mx-auto">
<a href="/" className="flex items-center">
<img src="https://flowbite.com/docs/images/logo.svg" className="h-6 mr-3 sm:h-9" alt="Flowbite Logo"/>
<span className="self-center text-xl font-semibold whitespace-nowrap dark:text-white">Flowbite</span>
</a>
<div className="flex items-center">
<button
className="text-gray-700 border border-transparent hover:bg-gray-200 hover:text-white focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm p-2.5 text-center inline-flex items-center mr-2 dark:text-blue-500 dark:hover:text-white dark:focus:ring-blue-800">
@ -208,15 +208,18 @@ export function Header({items = []}) {
</svg>
</button>
</div>
</div>
</section>
<hr className="border-gray-200 sm:mx-auto dark:border-gray-700"/>
<hr className="border-gray-100 sm:mx-auto dark:border-gray-700 container mx-auto"/>
<section className="hidden w-full md:block md:w-auto" id="navbar-dropdown">
<div className="container max-w-screen-xl mx-auto">
<ul className="flex flex-col md:p-4 font-medium md:flex-row md:space-x-8 md:mt-0">
{items.map((item, index: number) => renderGenerateItem(item, index))}
</ul>
</div>
</section>
<hr className="border-gray-100 sm:mx-auto dark:border-gray-700"/>
</main>
</div>
</nav>
);

@ -0,0 +1,7 @@
/*
* Replace this with your own classes
*
* e.g.
* .container {
* }
*/

@ -0,0 +1,10 @@
import { render } from '@testing-library/react';
import LastArticles from './last-articles';
describe('LastArticles', () => {
it('should render successfully', () => {
const { baseElement } = render(<LastArticles />);
expect(baseElement).toBeTruthy();
});
});

@ -0,0 +1,44 @@
import delve from 'dlv';
import styles from './last-articles.module.scss';
import {environment} from "../../environments/environment";
import {format} from "date-fns";
/* eslint-disable-next-line */
export interface LastArticlesProps {
items: object[];
}
export function LastArticles({items = []}: LastArticlesProps) {
return (
<div
className={styles['container'] + " bg-white rounded-lg mx-2 lg:mx-0 mt-5 lg:mt-0 p-5 mb-5 shadow-sm"}>
<div className="flex items-center justify-between mb-4">
<h5 className="text-xl font-bold leading-none text-gray-900 dark:text-white">Derniers articles publiés</h5>
</div>
<div className="flow-root">
<ul role="list" className="divide-y divide-gray-200 dark:divide-gray-700">
{items.map((item: object, index: number) => (<li key={'last-articles-' + index} className="py-3 sm:py-4">
<div className="flex items-center space-x-4">
<div className="flex-shrink-0">
<img className="w-[85px] h-[75px] rounded-lg"
src={environment.strapiUrl + delve(item, 'attributes.image.data.attributes.formats.medium.url', '')}
alt="Neil image"/>
</div>
<div className="flex-1 min-w-0">
<p className="text-xl font-medium text-gray-900 dark:text-white">
{delve(item, 'attributes.title', 'N/A')}
</p>
<p className="text-sm text-gray-500 dark:text-gray-400">
{delve(item, 'attributes.category.data.attributes.name', 'N/A')} - {format(new Date(delve(item, 'attributes.publishedAt', 'Jun 1, 2020')), 'dd/MM/yyyy')}
</p>
</div>
</div>
</li>))}
</ul>
</div>
</div>
);
}
export default LastArticles;

@ -3,7 +3,15 @@ import Footer from "../footer/footer";
import styles from './layout.module.scss';
export function Layout({showHeaders = true, showFooter = true, menuHeader = [], menuFooter = [], children = null, className = ''}) {
export function Layout({
showHeaders = true,
showFooter = true,
menuHeader = [],
menuFooter = [],
children = null,
className = '',
headerTransparent = false
}) {
const renderHeaders = () => {
if (showHeaders) {
@ -23,8 +31,9 @@ export function Layout({showHeaders = true, showFooter = true, menuHeader = [],
return (
<>
{renderHeaders()}
<div className={'dark:bg-gray-900 ' + styles['app-container'] + ' ' + (showHeaders ? '' : styles['without-headers'])+ ' ' + (showFooter ? '' : styles['without-footer']) + ' ' + className}>
<div className={styles['app-content'] + " container w-full mx-auto"}>
<div
className={'dark:bg-gray-900 bg-gray-50 ' + styles['app-container'] + ' ' + (showHeaders ? '' : styles['without-headers']) + ' ' + (showFooter ? '' : styles['without-footer']) + ' ' + className}>
<div className={styles['app-content'] + " w-full mx-auto"}>
{children}
</div>
{renderFooters()}

@ -1,5 +1,6 @@
export const environment = {
strapiUrl: 'http://127.0.0.1:1337/',
appUrl: 'http://localhost:4200',
strapiUrl: 'http://127.0.0.1:1337',
strapiApiUrl: 'http://127.0.0.1:1337/api',
nextAuthSecret: "yBNW1wrb9CgOvEfbX6EQCvCDqiMkRBZP"
}

@ -1,5 +1,6 @@
export const environment = {
strapiUrl: 'https://admin.mecp.nasercloud.fr/',
appUrl: 'http://localhost:4200',
strapiUrl: 'https://admin.mecp.nasercloud.fr',
strapiApiUrl: 'https://admin.mecp.nasercloud.fr/api',
nextAuthSecret: "yBNW1wrb9CgOvEfbX6EQCvCDqiMkRBZP"
}

@ -1,5 +1,6 @@
export const environment = {
strapiUrl: 'https://admin.mecp.nasercloud.fr/',
appUrl: 'http://localhost:4200',
strapiUrl: 'https://admin.mecp.nasercloud.fr',
strapiApiUrl: 'https://admin.mecp.nasercloud.fr/api',
nextAuthSecret: "yBNW1wrb9CgOvEfbX6EQCvCDqiMkRBZP"
}

@ -1,5 +1,6 @@
export const environment = {
strapiUrl: 'http://127.0.0.1:1337/',
appUrl: 'http://localhost:4200',
strapiUrl: 'http://127.0.0.1:1337',
strapiApiUrl: 'http://127.0.0.1:1337/api',
nextAuthSecret: "yBNW1wrb9CgOvEfbX6EQCvCDqiMkRBZP"
}

@ -15,6 +15,10 @@ const nextConfig = {
// See: https://github.com/gregberge/svgr
svgr: false
},
images: {
loader: "default",
domains: ["localhost", "127.0.0.1"],
},
};
module.exports = withNx(nextConfig);

@ -0,0 +1,57 @@
import axios from "axios";
import delve from 'dlv';
import {environment} from "../../../environments/environment";
import SeoConfig from "../../../components/seo-config/seo-config";
import Layout from "../../../components/layout/layout";
import styles from "../../index.module.scss";
import BlogSearch from "../../../components/blog-search/blog-search";
import Categories from "../../../components/categories/categories";
import LastArticles from "../../../components/last-articles/last-articles";
import CardBlogDetails from "../../../components/card-blog-details/card-blog-details";
export async function getServerSideProps(context) {
console.log(context);
const categories = await axios.get(`${environment.strapiApiUrl}/categories?populate=deep`);
const lastPublished = await axios.get(`${environment.strapiApiUrl}/articles?populate=deep&_sort=date:DESC`);
const post = await axios.get(`${environment.strapiApiUrl}/articles?populate=deep&filters[slug]=${context.params.post_slug}`);
if (!delve(post, "data.data.0.attributes", null)) {
return {
notFound: true
}
} else {
return {
props: {
categories: delve(categories, 'data.data', []),
lastPublished: delve(lastPublished, 'data.data', []),
post: delve(post, "data.data.0.attributes", null),
}
}
}
}
export function PostBlog({menuHeader, menuFooter, seo, categories, post, lastPublished}) {
return (
<>
<SeoConfig {...seo}/>
<Layout menuHeader={menuHeader} menuFooter={menuFooter} headerTransparent={true}>
<main className={styles['blog-container'] + " w-full lg:my-10"}>
<section className="container max-w-screen-xl mx-auto relative flex flex-col lg:flex-row">
<section className="grow lg:basis-4/6 mx-2 lg:mx-0 lg:pr-5">
<CardBlogDetails item={post}/>
</section>
<aside className="grow lg:basis-2/6">
<BlogSearch/>
<Categories items={categories}/>
<LastArticles items={lastPublished}/>
</aside>
</section>
</main>
</Layout>
</>
);
}
export default PostBlog;

@ -0,0 +1,77 @@
import axios from "axios";
import delve from "dlv";
import {environment} from "../../../environments/environment";
import SeoConfig from "../../../components/seo-config/seo-config";
import Layout from "../../../components/layout/layout";
import BlogSearch from "../../../components/blog-search/blog-search";
import Categories from "../../../components/categories/categories";
import LastArticles from "../../../components/last-articles/last-articles";
import styles from "../index.module.scss";
import CardBlog from "../../../components/card-blog/card-blog";
import BlogPagination from "../../../components/blog-pagination/blog-pagination";
export async function getServerSideProps(context) {
const {query} = context;
const categories = await axios.get(`${environment.strapiApiUrl}/categories?populate=deep`);
const category = await axios.get(`${environment.strapiApiUrl}/categories?populate=deep&filters[slug]=${context.params.category_slug}`);
const lastPublished = await axios.get(`${environment.strapiApiUrl}/articles?populate=deep&_sort=date:DESC`);
let postsUrl = `${environment.strapiApiUrl}/articles?populate=deep&_sort=date:DESC&filters[category][slug][$contains]=${context.params.category_slug}`;
if (query && query.page) {
postsUrl += `$pagination[page]=${query.page}`;
}
const posts = await axios.get(postsUrl);
if (!delve(category, "data.data.0.attributes", null)) {
return {
notFound: true
}
} else {
return {
props: {
categories: delve(categories, 'data.data', []),
category: delve(category, "data.data.0.attributes", null),
lastPublished: delve(lastPublished, 'data.data', []),
posts: delve(posts, 'data.data', []),
paginator: delve(posts, 'data.meta', []),
}
}
}
}
export function CategoryBlog({menuHeader, menuFooter, seo, categories, category, posts, lastPublished, paginator}) {
return (
<>
<SeoConfig {...seo}/>
<Layout menuHeader={menuHeader} menuFooter={menuFooter} headerTransparent={true}>
<main className={styles['blog-container'] + " w-full lg:my-10"}>
<section className="container max-w-screen-xl mx-auto relative flex">
<section className="grow lg:basis-4/6 mx-2 lg:mx-0 lg:pr-5">
<h3 className="text-2xl font-bold mb-5">{category.name}</h3>
{posts.length === 0 && (
<p>Aucun article n'a été posté dans cette catégorie pour l'instant</p>
)
}
{posts.length > 0 && posts.map((post, index: number) => (
<CardBlog key={'card-blog-' + post.id + index} item={post}/>))
}
{posts.length > 0 && (<footer className="mt-10">
<BlogPagination paginator={paginator}/>
</footer>)}
</section>
<aside className="grow lg:basis-2/6">
<BlogSearch/>
<Categories items={categories}/>
<LastArticles items={lastPublished}/>
</aside>
</section>
</main>
</Layout>
</>
);
}
export default CategoryBlog;

@ -0,0 +1,9 @@
/*
* Replace this with your own classes
*
* e.g.
* .container {
* }
*/
.blog-container {
}

@ -0,0 +1,47 @@
import axios from "axios";
import delve from "dlv";
import SeoConfig from "../../components/seo-config/seo-config";
import Layout from "../../components/layout/layout";
import Categories from "../../components/categories/categories";
import styles from "./index.module.scss";
import BlogSearch from "../../components/blog-search/blog-search";
import {environment} from "../../environments/environment";
import CardBlog from "../../components/card-blog/card-blog";
export async function getServerSideProps(context) {
const categories = await axios.get(`${environment.strapiApiUrl}/categories?populate=deep`)
const posts = await axios.get(`${environment.strapiApiUrl}/articles?populate=deep&_sort=date:DESC`)
return {
props: {
categories: delve(categories, 'data.data', []),
lastPublished: delve(posts, 'data.data', []),
}
}
}
export function Blog({menuHeader, menuFooter, seo, categories, lastPublished}) {
return (
<>
<SeoConfig {...seo}/>
<Layout menuHeader={menuHeader} menuFooter={menuFooter} headerTransparent={true}>
<main className={styles['blog-container'] + " w-full lg:my-10"}>
<section className="container mx-auto relative flex">
<section className="grow lg:basis-4/6 mx-2 lg:mx-0 lg:pr-5">
{lastPublished.map((post, index: number) => (
<CardBlog key={'card-blog-' + post.id + index} item={post}/>))
}
</section>
<aside className="grow lg:basis-2/6">
<BlogSearch/>
<Categories items={categories}/>
</aside>
</section>
</main>
</Layout>
</>
);
}
export default Blog;

@ -1,4 +1,4 @@
@import 'tailwindcss/base.css';
//@import 'tailwindcss/base.css';
@import 'tailwindcss/components.css';
@import 'tailwindcss/utilities.css';

@ -1,14 +1,8 @@
import axios from "axios";
import delve from "dlv";
import SeoConfig from "../components/seo-config/seo-config";
import Layout from "../components/layout/layout";
import {config} from "../config";
import {environment} from "../environments/environment";
export function Index({menuHeader, menuFooter, page, seo}) {
console.log(menuHeader, menuFooter);
return (
<>
<SeoConfig {...seo}/>

@ -24,7 +24,7 @@ export function SignIn({seo, menuFooter}) {
return (
<>
<SeoConfig {...seo}/>
<Layout showHeaders={false} showFooter={false} menuFooter={menuFooter} className="bg-gray-50">
<Layout showHeaders={false} showFooter={false} menuFooter={menuFooter}>
<div className={styles['page-sign-in']}>
<main className={styles['section']}>
<div className="container max-w-5xl px-6 py-4 md:py-12 h-full mx-auto relative -top-[25px] md:-top-[50px]">
@ -48,7 +48,7 @@ export function SignIn({seo, menuFooter}) {
</p>
</header>
<section
className="w-full max-w-[440px] p-4 bg-white border border-gray-200 rounded-lg shadow-md sm:p-6 md:p-8 dark:bg-gray-800 dark:border-gray-700">
className="w-full max-w-[440px] p-4 bg-white border border-gray-200 rounded-lg sm:p-6 md:p-8 dark:bg-gray-800 dark:border-gray-700">
<div className="w-full">
<form onSubmit={onSubmit}>
<div className="mb-6">

@ -6,6 +6,7 @@ html, body {
position: relative;
width: 100%;
height: 100%;
background: #f4f4f4;
}
body #__next {

13201
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -19,6 +19,7 @@
"@fortawesome/react-fontawesome": "0.2.0",
"@nrwl/next": "15.5.1",
"axios": "1.2.2",
"date-fns": "^2.29.3",
"dlv": "1.1.3",
"flowbite": "1.6.2",
"next": "13.1.1",
@ -27,7 +28,9 @@
"react-dom": "18.2.0",
"react-helmet": "6.0.0",
"react-hook-form": "7.42.1",
"react-share": "^4.4.1",
"rxjs": "7.8.0",
"tailwindcss": "3.2.4",
"tslib": "2.3.0"
},
"devDependencies": {
@ -64,7 +67,6 @@
"prettier": "2.6.2",
"react-test-renderer": "18.2.0",
"sass": "1.55.0",
"tailwindcss": "3.2.4",
"ts-jest": "28.0.5",
"ts-node": "10.9.1",
"typescript": "4.8.2"

Loading…
Cancel
Save