diff --git a/apps/website/.eslintrc.json b/apps/website/.eslintrc.json
index 8d6d104..276d251 100644
--- a/apps/website/.eslintrc.json
+++ b/apps/website/.eslintrc.json
@@ -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
diff --git a/apps/website/components/blog-pagination/blog-pagination.module.scss b/apps/website/components/blog-pagination/blog-pagination.module.scss
new file mode 100644
index 0000000..45c2aa4
--- /dev/null
+++ b/apps/website/components/blog-pagination/blog-pagination.module.scss
@@ -0,0 +1,7 @@
+/*
+ * Replace this with your own classes
+ *
+ * e.g.
+ * .container {
+ * }
+*/
diff --git a/apps/website/components/blog-pagination/blog-pagination.spec.tsx b/apps/website/components/blog-pagination/blog-pagination.spec.tsx
new file mode 100644
index 0000000..f4cca7b
--- /dev/null
+++ b/apps/website/components/blog-pagination/blog-pagination.spec.tsx
@@ -0,0 +1,10 @@
+import { render } from '@testing-library/react';
+
+import BlogPagination from './blog-pagination';
+
+describe('BlogPagination', () => {
+ it('should render successfully', () => {
+ const { baseElement } = render();
+ expect(baseElement).toBeTruthy();
+ });
+});
diff --git a/apps/website/components/blog-pagination/blog-pagination.tsx b/apps/website/components/blog-pagination/blog-pagination.tsx
new file mode 100644
index 0000000..0593cd1
--- /dev/null
+++ b/apps/website/components/blog-pagination/blog-pagination.tsx
@@ -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 (
+
+
+ {isOnlyOne && !isEmpty && (
+
{total} article(s))}
+ {!isOnlyOne && !isEmpty && (
+ Articles {page} à {pageCount} sur un total de {total}
+ )}
+
+
+
+
+
+
+ );
+}
+
+export default BlogPagination;
diff --git a/apps/website/components/blog-search/blog-search.module.scss b/apps/website/components/blog-search/blog-search.module.scss
new file mode 100644
index 0000000..45c2aa4
--- /dev/null
+++ b/apps/website/components/blog-search/blog-search.module.scss
@@ -0,0 +1,7 @@
+/*
+ * Replace this with your own classes
+ *
+ * e.g.
+ * .container {
+ * }
+*/
diff --git a/apps/website/components/blog-search/blog-search.spec.tsx b/apps/website/components/blog-search/blog-search.spec.tsx
new file mode 100644
index 0000000..3f665ab
--- /dev/null
+++ b/apps/website/components/blog-search/blog-search.spec.tsx
@@ -0,0 +1,10 @@
+import { render } from '@testing-library/react';
+
+import BlogSearch from './blog-search';
+
+describe('BlogSearch', () => {
+ it('should render successfully', () => {
+ const { baseElement } = render();
+ expect(baseElement).toBeTruthy();
+ });
+});
diff --git a/apps/website/components/blog-search/blog-search.tsx b/apps/website/components/blog-search/blog-search.tsx
new file mode 100644
index 0000000..d3b2b03
--- /dev/null
+++ b/apps/website/components/blog-search/blog-search.tsx
@@ -0,0 +1,115 @@
+import {useEffect, useState} from "react";
+import {BehaviorSubject, catchError, debounceTime, delay, distinctUntilChanged, filter, map, of, switchMap} from "rxjs";
+import {useRouter} from "next/router";
+import {MeiliSearch} from "meilisearch";
+import {useDispatch} from "react-redux";
+
+import {environment} from "../../environments/environment";
+
+import styles from './blog-search.module.scss';
+import {setSearchState} from "../../store/searchSlice";
+
+/* eslint-disable-next-line */
+export interface BlogSearchProps {
+}
+
+export function BlogSearch(props: BlogSearchProps) {
+ const router = useRouter();
+ const [client, setClient] = useState(null);
+ const [loading, setLoading] = useState(false);
+ const [subject, setSubject] = useState(null);
+ const dispatch = useDispatch();
+
+ useEffect(() => {
+ if (subject === null) {
+ const sub = new BehaviorSubject('');
+ const client = new MeiliSearch({
+ host: 'http://127.0.0.1:7700',
+ apiKey: environment.meiliMasterKey
+ })
+ setSubject(sub);
+ setClient(client);
+ } else {
+ subject.pipe(
+ map((s: string) => s.trim()),
+ distinctUntilChanged(),
+ filter((s: string) => s.length >= 2),
+ debounceTime(200),
+ map((value) => {
+ setLoading(true);
+ return value;
+ }),
+ delay(2000),
+ switchMap(async (value: string) => {
+ const index = await client.getIndex('article');
+ const articles = await index.search(value);
+ setLoading(false);
+ await router.push('/search');
+ dispatch(setSearchState(articles.hits));
+ }),
+ catchError((err) => of(false))
+ ).subscribe(() => setLoading(false));
+ }
+ }, [subject]);
+
+ const onSearchChange = (e) => subject.next(e.target.value);
+
+ return (
+
+ );
+}
+
+export default BlogSearch;
diff --git a/apps/website/components/card-blog-details/card-blog-details.module.scss b/apps/website/components/card-blog-details/card-blog-details.module.scss
new file mode 100644
index 0000000..45c2aa4
--- /dev/null
+++ b/apps/website/components/card-blog-details/card-blog-details.module.scss
@@ -0,0 +1,7 @@
+/*
+ * Replace this with your own classes
+ *
+ * e.g.
+ * .container {
+ * }
+*/
diff --git a/apps/website/components/card-blog-details/card-blog-details.spec.tsx b/apps/website/components/card-blog-details/card-blog-details.spec.tsx
new file mode 100644
index 0000000..75c020e
--- /dev/null
+++ b/apps/website/components/card-blog-details/card-blog-details.spec.tsx
@@ -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();
+ expect(baseElement).toBeTruthy();
+ });
+});
diff --git a/apps/website/components/card-blog-details/card-blog-details.tsx b/apps/website/components/card-blog-details/card-blog-details.tsx
new file mode 100644
index 0000000..6d849c1
--- /dev/null
+++ b/apps/website/components/card-blog-details/card-blog-details.tsx
@@ -0,0 +1,110 @@
+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}`;
+ return (
+
+
+
+ {delve(item, 'title', 'N/A')}
+
+
+
+
+
+
+
+
+
+
+
Partager cet article
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default CardBlogDetails;
diff --git a/apps/website/components/card-blog/card-blog.module.scss b/apps/website/components/card-blog/card-blog.module.scss
new file mode 100644
index 0000000..45c2aa4
--- /dev/null
+++ b/apps/website/components/card-blog/card-blog.module.scss
@@ -0,0 +1,7 @@
+/*
+ * Replace this with your own classes
+ *
+ * e.g.
+ * .container {
+ * }
+*/
diff --git a/apps/website/components/card-blog/card-blog.spec.tsx b/apps/website/components/card-blog/card-blog.spec.tsx
new file mode 100644
index 0000000..7068caf
--- /dev/null
+++ b/apps/website/components/card-blog/card-blog.spec.tsx
@@ -0,0 +1,10 @@
+import { render } from '@testing-library/react';
+
+import CardBlog from './card-blog';
+
+describe('CardBlog', () => {
+ it('should render successfully', () => {
+ const { baseElement } = render();
+ expect(baseElement).toBeTruthy();
+ });
+});
diff --git a/apps/website/components/card-blog/card-blog.tsx b/apps/website/components/card-blog/card-blog.tsx
new file mode 100644
index 0000000..d3aa0a9
--- /dev/null
+++ b/apps/website/components/card-blog/card-blog.tsx
@@ -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 (
+
+
+
+

+
+
+
+
+
+
+ );
+}
+
+export default CardBlog;
diff --git a/apps/website/components/categories/categories.module.scss b/apps/website/components/categories/categories.module.scss
new file mode 100644
index 0000000..45c2aa4
--- /dev/null
+++ b/apps/website/components/categories/categories.module.scss
@@ -0,0 +1,7 @@
+/*
+ * Replace this with your own classes
+ *
+ * e.g.
+ * .container {
+ * }
+*/
diff --git a/apps/website/components/categories/categories.spec.tsx b/apps/website/components/categories/categories.spec.tsx
new file mode 100644
index 0000000..deaa1e1
--- /dev/null
+++ b/apps/website/components/categories/categories.spec.tsx
@@ -0,0 +1,10 @@
+import { render } from '@testing-library/react';
+
+import Categories from './categories';
+
+describe('Categories', () => {
+ it('should render successfully', () => {
+ const { baseElement } = render();
+ expect(baseElement).toBeTruthy();
+ });
+});
diff --git a/apps/website/components/categories/categories.tsx b/apps/website/components/categories/categories.tsx
new file mode 100644
index 0000000..e31cdce
--- /dev/null
+++ b/apps/website/components/categories/categories.tsx
@@ -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[];
+}
+
+//
+
+export function Categories({items = []}: CategoriesProps) {
+ return (
+
+
+
Catégories
+
+
+
+ {items.map((item: any, index: number) => (-
+
+
+

+
+
+
{delve(item, 'attributes.name', 'N/A')}
+
+
+ ))
+ }
+
+
+
+ );
+}
+
+export default Categories;
diff --git a/apps/website/components/footer/footer.tsx b/apps/website/components/footer/footer.tsx
index b76978b..dd74c44 100644
--- a/apps/website/components/footer/footer.tsx
+++ b/apps/website/components/footer/footer.tsx
@@ -15,7 +15,7 @@ export function Footer({items = []}) {
}
return (
-