diff --git a/back/src/main.rs b/back/src/main.rs index ad8f7f5..7242d32 100644 --- a/back/src/main.rs +++ b/back/src/main.rs @@ -74,7 +74,7 @@ async fn main() -> Result<(), std::io::Error> { .service(api) .service(Files::new("/", "public").index_file("index.html")) }) - .bind(("0.0.0.0", 8080))? + .bind(("0.0.0.0", 2486))? .run() .await } diff --git a/docker-compose.yml b/docker-compose.yml index 1e0df8d..9bcaab9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ services: container_name: web restart: always ports: - - 8080:8080 + - 8080:2486 volumes: - ./back/data:/app/data diff --git a/dockerfile b/dockerfile index d8e8f78..f092a14 100644 --- a/dockerfile +++ b/dockerfile @@ -10,9 +10,9 @@ RUN cargo build --release FROM debian:bookworm-slim WORKDIR /app -RUN apt-get update & apt-get install -y extra-runtime-dependencies & rm -rf /var/lib/apt/lists/* +RUN apt update && apt install -y libsqlite3-0 COPY --from=front /app/dist /app/public COPY --from=back /app/target/release/back /app/back -EXPOSE 8080 +EXPOSE 2486 CMD ["/app/back"] diff --git a/front/package.json b/front/package.json index 8bd44d7..e1132b0 100644 --- a/front/package.json +++ b/front/package.json @@ -12,7 +12,8 @@ "dependencies": { "react": "^18.3.1", "react-dom": "^18.3.1", - "react-router": "^7.0.2" + "react-router": "^7.0.2", + "react-router-dom": "^6.2.1" }, "devDependencies": { "@eslint/js": "^9.15.0", @@ -25,6 +26,7 @@ "globals": "^15.12.0", "typescript": "~5.6.2", "typescript-eslint": "^8.15.0", - "vite": "^6.0.1" + "vite": "^6.0.1", + "react-router-dom": "^6.2.1" } } diff --git a/front/public/pictures/sea.gif b/front/public/pictures/sea.gif new file mode 100644 index 0000000..78a4617 Binary files /dev/null and b/front/public/pictures/sea.gif differ diff --git a/front/src/components/ArticleCard.tsx b/front/src/components/ArticleCard.tsx new file mode 100644 index 0000000..6a02bb3 --- /dev/null +++ b/front/src/components/ArticleCard.tsx @@ -0,0 +1,49 @@ +import { ArticlePreview } from '../types' +import ButtonLink from './ButtonLink' + +export default function ArticleCard({ articlePreview }: { articlePreview: ArticlePreview }) { + return ( +
+ Article preview +
+

{articlePreview.title}

+

{articlePreview.preview}

+
+
+
+ ) +} \ No newline at end of file diff --git a/front/src/components/ArticlesSection.tsx b/front/src/components/ArticlesSection.tsx new file mode 100644 index 0000000..a7b5df2 --- /dev/null +++ b/front/src/components/ArticlesSection.tsx @@ -0,0 +1,48 @@ +import { useEffect, useState } from "react"; +import ArticleCard from "./ArticleCard"; +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/articles') + .then(response => response.json()) + .then((data: ArticlePreview[]) => setArticlePreviews(data)) + .catch(_ => setError('Failed to fetch articles')) + .finally(() => setLoading(false)); + }, []); + + return ( +
+

Articles

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

No articles found

} + {loading &&

Loading...

} + {error &&

{error}

} +
+ ) +} diff --git a/front/src/components/Button.tsx b/front/src/components/Button.tsx new file mode 100644 index 0000000..654bda5 --- /dev/null +++ b/front/src/components/Button.tsx @@ -0,0 +1,35 @@ +import { ReactNode, MouseEventHandler } from 'react'; + +interface ButtonProps { + onClick: MouseEventHandler; + color: 'primary' | 'secondary'; + children: ReactNode; +} + +export default function Button({ onClick, color, children }: ButtonProps) { + return ( + + ) +} + 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/components/ClickableLink.tsx b/front/src/components/ClickableLink.tsx new file mode 100644 index 0000000..0096871 --- /dev/null +++ b/front/src/components/ClickableLink.tsx @@ -0,0 +1,31 @@ +interface ClickableLinkProps { + url:string; + text:string; +} + +export default function ClickableLink({url, text}: ClickableLinkProps) { + return (
+ +
) +} \ No newline at end of file diff --git a/front/src/components/Footer.tsx b/front/src/components/Footer.tsx new file mode 100644 index 0000000..468812c --- /dev/null +++ b/front/src/components/Footer.tsx @@ -0,0 +1,19 @@ +import LogoButton from "./LogoButton" + +interface FooterProps { + bgcolor:string +} + +export default function Footer ({bgcolor}:FooterProps) { + return ( +
+
ENSIBS
RedCRAB
+
+ +
+
+ ) +} \ No newline at end of file diff --git a/front/src/components/FstSection.tsx b/front/src/components/FstSection.tsx new file mode 100644 index 0000000..a978737 --- /dev/null +++ b/front/src/components/FstSection.tsx @@ -0,0 +1,38 @@ +import Button from "./Button"; +import NavBar from "./NavBar"; + +interface FstSectionProps { + centertxt: string; + txtbt1:string; + txtbt2:string; + image:string; +} + +export default function FstSection ({centertxt, txtbt1, txtbt2, image}: FstSectionProps) { + return ( +
+ +
+
+

{centertxt}


+
+
+
+ ) +} \ No newline at end of file diff --git a/front/src/components/LogoButton.tsx b/front/src/components/LogoButton.tsx new file mode 100644 index 0000000..bc41d3c --- /dev/null +++ b/front/src/components/LogoButton.tsx @@ -0,0 +1,13 @@ +import * as React from 'react'; + +interface LogoButtonProps { + url:string; + logo:string; + style?: React.CSSProperties; +} + +export default function LogoButton ({url, logo, style}: LogoButtonProps) { + return ( + + ) +} \ No newline at end of file diff --git a/front/src/components/NavBar.tsx b/front/src/components/NavBar.tsx new file mode 100644 index 0000000..4f1c35d --- /dev/null +++ b/front/src/components/NavBar.tsx @@ -0,0 +1,17 @@ +import LogoButton from '../components/LogoButton.tsx' +import ClickableLink from './ClickableLink.tsx'; +import RoundButton from './RoundButton.tsx'; + +export default function NavBar(){ + return ( + ); +} \ No newline at end of file diff --git a/front/src/components/RoundButton.tsx b/front/src/components/RoundButton.tsx new file mode 100644 index 0000000..4747d57 --- /dev/null +++ b/front/src/components/RoundButton.tsx @@ -0,0 +1,32 @@ +interface RoundButtonProps { + url:string; + bgcolor:string; + text:string; +} + +export default function RoundButton({url, bgcolor, text}: RoundButtonProps) { + return (
+ +
) +} \ No newline at end of file diff --git a/front/src/index.css b/front/src/index.css new file mode 100644 index 0000000..328146f --- /dev/null +++ b/front/src/index.css @@ -0,0 +1,19 @@ +/* Define theme colors */ +:root { + --color-verydarkblue: #00204a; + --color-darkblue: #005792; + --color-lightblue: #00bbf0; + --color-yellow: #fdb44b; + --color-white: #ffffff; + --color-black: #000000; + --color-gray: #f5f5f5; + --color-red: #ff0000; +} + +body { + margin: 0; +} + +footer { + text-align: center; +} \ No newline at end of file diff --git a/front/src/main.tsx b/front/src/main.tsx index ce00332..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..3ab05f8 --- /dev/null +++ b/front/src/pages/ArticlePage.tsx @@ -0,0 +1,78 @@ +import React, { useEffect, useState } from 'react'; +import { useParams } from 'react-router-dom'; +import NavBar from '../components/NavBar'; +import Footer from '../components/Footer'; + +interface Article { + id: string; + title: string; + content: string; + author: string; + publishedAt: string; + editedAt: 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}

+

écrit par {article.author} le {new Date(article.publishedAt).toLocaleDateString()} (Dernière modification le : {new Date(article.editedAt || article.publishedAt).toLocaleDateString()})

+
+

{article.content}

+
+
+
+
+ ); +}; + +export default ArticlePage; \ No newline at end of file diff --git a/front/src/pages/MainPage.tsx b/front/src/pages/MainPage.tsx index 248feb2..a64aeef 100644 --- a/front/src/pages/MainPage.tsx +++ b/front/src/pages/MainPage.tsx @@ -1,10 +1,19 @@ - - +import ArticlesSection from '../components/ArticlesSection.tsx' +import Footer from '../components/Footer.tsx' +import FstSection from '../components/FstSection.tsx' export default function MainPage() { return ( -
-

Main Page

+ <> +
+ + +
+ ) } \ No newline at end of file diff --git a/front/src/types.ts b/front/src/types.ts new file mode 100644 index 0000000..b86ff18 --- /dev/null +++ b/front/src/types.ts @@ -0,0 +1,13 @@ +interface Article { + id: number; + title: string; + content: string; +} + +interface ArticlePreview { + id: number; + title: string; + preview: string; +} + +export type { Article, ArticlePreview }; \ No newline at end of file diff --git a/front/vite.config.ts b/front/vite.config.ts index 8b0f57b..795d340 100644 --- a/front/vite.config.ts +++ b/front/vite.config.ts @@ -4,4 +4,28 @@ import react from '@vitejs/plugin-react' // https://vite.dev/config/ export default defineConfig({ plugins: [react()], + server: { + proxy: { + '/api': { + target: 'http://127.0.0.1:2486', + changeOrigin: true, + secure: false, + ws: true, + /* + configure: (proxy, _options) => { + proxy.on('error', (err, _req, _res) => { + console.log('proxy error', err); + }); + proxy.on('proxyReq', (proxyReq, req, _res) => { + console.log('Sending Request to the Target:', req.method, req.url); + }); + proxy.on('proxyRes', (proxyRes, req, _res) => { + console.log('Received Response from the Target:', proxyRes.statusCode, req.url); + }); + }, + */ + //rewrite: path => path.replace(/^\/api/, '') + } + } + }, }) diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..e73376c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "projet-nuitinfo-2024", + "lockfileVersion": 3, + "requires": true, + "packages": {} +}