From e121090433fee24b8cb9a3115baf50cf4ff189e8 Mon Sep 17 00:00:00 2001 From: = Date: Fri, 6 Dec 2024 01:28:36 +0100 Subject: [PATCH] Make a better article section and add the article page. --- front/src/components/ArticleCard.tsx | 4 +- front/src/components/ArticlesSection.tsx | 31 +++++--------- front/src/components/ButtonLink.tsx | 34 +++++++++++++++ front/src/index.css | 1 + front/src/main.tsx | 9 +++- front/src/pages/ArticlePage.tsx | 54 ++++++++++++++++++++++++ front/src/pages/MainPage.tsx | 4 +- 7 files changed, 111 insertions(+), 26 deletions(-) create mode 100644 front/src/components/ButtonLink.tsx create mode 100644 front/src/pages/ArticlePage.tsx diff --git a/front/src/components/ArticleCard.tsx b/front/src/components/ArticleCard.tsx index 6764b17..6a02bb3 100644 --- a/front/src/components/ArticleCard.tsx +++ b/front/src/components/ArticleCard.tsx @@ -1,5 +1,5 @@ import { ArticlePreview } from '../types' -import Button from './Button' +import ButtonLink from './ButtonLink' export default function ArticleCard({ articlePreview }: { articlePreview: ArticlePreview }) { return ( @@ -42,7 +42,7 @@ export default function ArticleCard({ articlePreview }: { articlePreview: Articl lineClamp: 3, WebkitBoxOrient: 'vertical', }}>{articlePreview.preview}

-
+
) diff --git a/front/src/components/ArticlesSection.tsx b/front/src/components/ArticlesSection.tsx index eab4b52..fe52486 100644 --- a/front/src/components/ArticlesSection.tsx +++ b/front/src/components/ArticlesSection.tsx @@ -5,30 +5,15 @@ import { ArticlePreview } from "../types"; export default function ArticlesSection() { const [articlePreviews, setArticlePreviews] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); useEffect(() => { fetch('/api/article') - .then(response => { - //response.json() - return [ - { - id: 1, - title: "Qu'est-ce que le Lorem Ipsum?", - preview: "Le Lorem Ipsum est simplement du faux texte employé dans la composition et la mise en page avant impression. Le Lorem Ipsum est le faux texte standard de l'imprimerie depuis les années 1500, quand un imprimeur anonyme assembla ensemble des morceaux de texte pour réaliser un livre spécimen de polices de texte. Il n'a pas fait que survivre cinq siècles, mais s'est aussi adapté à la bureautique informatique, sans que son contenu n'en soit modifié. Il a été popularisé dans les années 1960 grâce à la vente de feuilles Letraset contenant des passages du Lorem Ipsum, et, plus récemment, par son inclusion dans des applications de mise en page de texte, comme Aldus PageMaker.", - }, - { - id: 2, - title: "Pourquoi l'utiliser?", - preview: "On sait depuis longtemps que travailler avec du texte lisible et contenant du sens est source de distractions, et empêche de se concentrer sur la mise en page elle-même. L'avantage du Lorem Ipsum sur un texte générique comme 'Du texte. Du texte. Du texte.' est qu'il possède une distribution de lettres plus ou moins normale, et en tout cas comparable avec celle du français standard. De nombreuses suites logicielles de mise en page ou éditeurs de sites Web ont fait du Lorem Ipsum leur faux texte par défaut, et une recherche pour 'Lorem Ipsum' vous conduira vers de nombreux sites qui n'en sont encore qu'à leur phase de construction. Plusieurs versions sont apparues avec le temps, parfois par accident, souvent intentionnellement (histoire d'y rajouter de petits clins d'oeil, voire des phrases embarassantes).", - }, - { - id: 3, - title: "Title", - preview: "Preview", - }, - ] - }) - .then((data: ArticlePreview[]) => setArticlePreviews(data)); + .then(response => response.json()) + .then((data: ArticlePreview[]) => setArticlePreviews(data)) + .catch(_ => setError('Failed to fetch articles')) + .finally(() => setLoading(false)); }, []); return ( @@ -37,6 +22,7 @@ export default function ArticlesSection() { flexWrap: 'wrap', justifyContent: 'space-around', backgroundColor: 'var(--color-verydarkblue)', + flex: 1, }}>

( ))} + {!loading && !error && articlePreviews.length === 0 &&

No articles found

} + {loading &&

Loading...

} + {error &&

{error}

} ) } diff --git a/front/src/components/ButtonLink.tsx b/front/src/components/ButtonLink.tsx new file mode 100644 index 0000000..a8a4757 --- /dev/null +++ b/front/src/components/ButtonLink.tsx @@ -0,0 +1,34 @@ +interface ButtonLinkProps { + url: string; + color: 'primary' | 'secondary'; + text: string; +} + +export default function Button({ url, color, text }: ButtonLinkProps) { + return ( + {text} + ) +} + diff --git a/front/src/index.css b/front/src/index.css index 3b3b175..328146f 100644 --- a/front/src/index.css +++ b/front/src/index.css @@ -7,6 +7,7 @@ --color-white: #ffffff; --color-black: #000000; --color-gray: #f5f5f5; + --color-red: #ff0000; } body { diff --git a/front/src/main.tsx b/front/src/main.tsx index f2884e0..d4ac604 100644 --- a/front/src/main.tsx +++ b/front/src/main.tsx @@ -2,13 +2,20 @@ import { BrowserRouter, Route, Routes } from "react-router"; import { createRoot } from 'react-dom/client' import MainPage from "./pages/MainPage.tsx"; import GamePage from "./pages/GamePage.tsx"; +import ArticlePage from "./pages/ArticlePage.tsx"; import './index.css' createRoot(document.getElementById('root')!).render( + // Main page } /> + // Game page } /> + // Article page (dynamic route) + } /> + // Not found + Not Found} /> - , + ) diff --git a/front/src/pages/ArticlePage.tsx b/front/src/pages/ArticlePage.tsx new file mode 100644 index 0000000..b3d99ad --- /dev/null +++ b/front/src/pages/ArticlePage.tsx @@ -0,0 +1,54 @@ +import React, { useEffect, useState } from 'react'; +import { useParams } from 'react-router-dom'; + +interface Article { + id: string; + title: string; + content: string; + author: string; + publishedAt: string; +} + +const ArticlePage: React.FC = () => { + const { id } = useParams<{ id: string }>(); + const [article, setArticle] = useState
(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + fetch(`/api/article/${id}`) + .then(response => response.json()) + .then(data => setArticle(data)) + .catch(_ => setError('Failed to fetch article')) + .finally(() => setLoading(false)); + }, [id]); + + if (loading) { + return
Loading...
; + } + + if (error) { + return
{error}
; + } + + if (!article) { + return
Article not found
; + } + + return ( +
+ Article preview +

{article.title}

+

{article.content}

+

By {article.author}

+

Published on {new Date(article.publishedAt).toLocaleDateString()}

+
+ ); +}; + +export default ArticlePage; \ No newline at end of file diff --git a/front/src/pages/MainPage.tsx b/front/src/pages/MainPage.tsx index be1d97d..f051552 100644 --- a/front/src/pages/MainPage.tsx +++ b/front/src/pages/MainPage.tsx @@ -5,11 +5,11 @@ import Footer from '../components/Footer.tsx' export default function MainPage() { return ( <> -
+
+
-