Skip to content

Commit

Permalink
feat(#63): create article page with content
Browse files Browse the repository at this point in the history
  • Loading branch information
Boyadjie committed Jun 30, 2024
1 parent 29d0089 commit 146a601
Show file tree
Hide file tree
Showing 8 changed files with 260 additions and 5 deletions.
110 changes: 110 additions & 0 deletions app/articles/[id]/page.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
.loader {
width: 100%;
display: flex;
justify-content: center;

svg {
width: 120px;
}
}

.actions {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 35px;
}

.article {
display: flex;
flex-direction: column;
align-items: center;
gap: 20px;
padding-bottom: 120px;

> img {
border-radius: 20px;
}

.content {
padding: 0 35px;

h1 {
text-align: center;
font-size: 18px;
font-weight: bold;
}

.details {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 0;

> p {
font-size: 11px;
color: #7f7f7f;
}

.rate {
display: block;
width: max-content;
color: white;
background: var(--primary);
padding: 4px 12px;
border-radius: 50px;
font-size: 11px;
}
}

section {
display: flex;
flex-direction: column;
gap: 30px;
padding-top: 30px;

.infoBloc {
.blocHeader {
display: flex;
align-items: center;
gap: 22px;
width: 100%;
color: var(--primary);

> p {
font-size: 22px;
font-weight: bold;
}

> h3 {
font-size: 14px;
font-weight: 500;
width: 75%;
line-height: 1.5;
}

button {
border: none;
background-color: white;
border-radius: 50px;
cursor: pointer;
width: 25px;
height: 25px;
display: flex;
justify-content: center;
align-items: center;
outline: 1px solid var(--primary);
}
}

ul {
padding-left: 25px;
padding-top: 12px;
li {
font-size: 12px;
}
}
}
}
}
}
105 changes: 105 additions & 0 deletions app/articles/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
'use client';
import {useEffect, useState} from 'react';

import {format} from 'date-fns';
import Image from 'next/image';
import Link from 'next/link';

import {Loader} from '../../../src/components/decorations/Loader';
import {Navbar} from '../../../src/components/navbar/Navbar';
import {useArticleContent} from '../../../src/hook/useArticleContent';
import styles from './page.module.css';

type PageParameters = {
params: {
id: string;
};
};

export default function Articles({params}: PageParameters) {
const {id} = params;
const {loading, article} = useArticleContent(id);
const [formatedTitleImg, setFormatedTitleImg] = useState<string>('');

useEffect(() => {
if (!loading && article) {
setFormatedTitleImg(
article.title
.toLowerCase()
.replaceAll(': ', '')
.replaceAll(' ?', '')
.replaceAll('’', '_')
.replaceAll('&', 'et')
.replaceAll(' ', '_'),
);
}
}, [loading, article]);

return (
<>
<Navbar />
<div className={styles.actions}>
<Link href="/articles">
<Image src="/longArrow.svg" alt="note" width={24} height={18} />
</Link>
<Image src="/icons/coeur.svg" alt="coeur" width={24} height={24} />
</div>
{loading && (
<div className={styles.loader}>
<Loader />
</div>
)}
{!loading && article && (
<article className={styles.article}>
<Image
src={`/images/articles/${formatedTitleImg}.webp`}
alt={article.title}
width={360}
height={185}
/>

<div className={styles.content}>
<h1>{article.title.toUpperCase()}</h1>

<div className={styles.details}>
<p>{format(article.date, 'd MMM. yyyy')}</p>
<span className={styles.rate}>
<Image
src="/icons/map/recommendations/like_blanc.svg"
alt="note"
width={10}
height={10}
/>{' '}
{article.rate * 100}%
</span>
</div>

<section>
{article.content.map(({subtitle, content}, index) => (
<div key={index} className={styles.infoBloc}>
<div className={styles.blocHeader}>
<p>{index + 1}</p>
<h3>{subtitle}</h3>
<button>
<Image
src="/icons/coeur.svg"
alt="coeur"
width={17}
height={17}
/>
</button>
</div>
<ul>
{content.map((info, index) => (
<li key={index}>{info}</li>
))}
</ul>
</div>
))}
</section>
</div>
</article>
)}
</>
);
}
3 changes: 3 additions & 0 deletions public/icons/map/recommendations/like_blanc.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/longArrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 5 additions & 3 deletions src/components/articles/articlesList/ArticlesList.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import Link from 'next/link';

import {ArticleDb} from '../../../types/Article';
import {ArticlePreview} from '../../blocs/preview/article/ArticlePreview';
import styles from './articlesList.module.css';
Expand All @@ -8,7 +10,7 @@ export const ArticlesList: React.FC<{articles: ArticleDb[]}> = ({articles}) => {
<p className={styles.length}>{articles.length} articles</p>

<div className={styles.articlesList}>
{articles.map(({title, date, rate}, index) => {
{articles.map(({title, date, rate, id}, index) => {
const formatedDate = new Date(date);
const formatedTitleImg = title
.toLowerCase()
Expand All @@ -19,15 +21,15 @@ export const ArticlesList: React.FC<{articles: ArticleDb[]}> = ({articles}) => {
.replaceAll(' ', '_');

return (
<div key={index} className={styles.card}>
<Link href={`/articles/${id}`} key={index} className={styles.card}>
<ArticlePreview
title={title}
date={formatedDate}
rate={rate}
pictureUrl={`/images/articles/${formatedTitleImg}.webp`}
fullWidth={true}
/>
</div>
</Link>
);
})}
</div>
Expand Down
25 changes: 25 additions & 0 deletions src/hook/useArticleContent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {useEffect, useState} from 'react';

import {ArticleDb} from '../types/Article';
import {useArticles} from './useArticles';

export const useArticleContent = (id: string) => {
const {loading, categories} = useArticles();
const [loadingArticle, setLoadingArticle] = useState<boolean>(false);
const [article, setArticle] = useState<ArticleDb>();

useEffect(() => {
setLoadingArticle(true);

if (!loading && categories) {
const allArticles: ArticleDb[] = Object.values(categories).flat();
const foundArticle = allArticles.find(
(article: ArticleDb) => article.id === id,
);
setArticle(foundArticle);
setLoadingArticle(false);
}
}, [loading, categories, id]);

return {loading: loadingArticle, article};
};
10 changes: 8 additions & 2 deletions src/hook/useArticles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,24 @@ type CategorisedArticles = {
expert: ArticleDb[];
};

type ArticleList = {data: string; category: string; id: number}[];
type ArticleList = {data: string; category: string; id: string}[];

const formatArticleList = (dataList: ArticleList) => {
const formatedList = dataList.map((data) => {
return JSON.parse(data.data) as ArticleDb;
const parsedData = JSON.parse(data.data);
const formatedArticle: ArticleDb = {
id: data.id,
...parsedData,
};
return formatedArticle;
});

return formatedList;
};

export const useArticles = () => {
const articlesData = useFirestore('articles');

const [categorisedArticles, setCategorisedArticles] =
useState<CategorisedArticles>();

Expand Down
1 change: 1 addition & 0 deletions src/types/Article.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type ArticleProps = {
};

export type ArticleDb = {
id: string;
date: string;
rate: number;
title: string;
Expand Down

0 comments on commit 146a601

Please sign in to comment.