feat: ajouter des liens GitHub aux pages de projet et implémenter le composant PastilleStatut pour le suivi de l'état en ligne

This commit is contained in:
Puechberty Arthur
2026-03-31 17:10:39 +02:00
parent 13c171e466
commit 6e2147a489
19 changed files with 92 additions and 20 deletions
+1
View File
@@ -44,6 +44,7 @@ export default function BlocNoteInfo() {
]}
images={["/placeholder-blocnote.webp", "/placeholder-blocnote-2.webp", "/placeholder-blocnote-3.webp"]}
url="https://blocnote.arthurp.fr"
githubUrl="https://github.com/arthur-pbty/blocnote"
/>
);
}
+1
View File
@@ -43,6 +43,7 @@ export default function CalculatriceInfo() {
]}
images={["/placeholder-calculatrice.webp", "/placeholder-calculatrice-2.webp", "/placeholder-calculatrice-3.webp"]}
url="https://calculatrice.arthurp.fr"
githubUrl="https://github.com/arthur-pbty/calculatrice"
/>
);
}
+1
View File
@@ -45,6 +45,7 @@ export default function ChronoInfo() {
]}
images={["/placeholder-chrono.webp", "/placeholder-chrono-2.webp", "/placeholder-chrono-3.webp"]}
url="https://chrono.arthurp.fr"
githubUrl="https://github.com/arthur-pbty/chrono"
/>
);
}
+1
View File
@@ -43,6 +43,7 @@ export default function ClockInfo() {
]}
images={["/placeholder-clock.webp", "/placeholder-clock-2.webp", "/placeholder-clock-3.webp"]}
url="https://clock.arthurp.fr"
githubUrl="https://github.com/arthur-pbty/clock"
/>
);
}
+1
View File
@@ -45,6 +45,7 @@ export default function FormCraftInfo() {
]}
images={["/placeholder-formcraft.webp", "/placeholder-formcraft-2.webp", "/placeholder-formcraft-3.webp"]}
url="https://form.arthurp.fr/"
githubUrl="https://github.com/arthur-pbty/form"
/>
);
}
+1
View File
@@ -43,6 +43,7 @@ export default function ImprimerSudokuInfo() {
]}
images={["/placeholder-imprimersudoku.webp", "/placeholder-imprimersudoku-2.webp", "/placeholder-imprimersudoku-3.webp"]}
url="https://imprimersudoku.arthurp.fr"
githubUrl="https://github.com/arthur-pbty/imprimersudoku"
/>
);
}
+1
View File
@@ -44,6 +44,7 @@ export default function LazyBotInfo() {
]}
images={["/placeholder-lazybot.webp", "/placeholder-lazybot-2.webp", "/placeholder-lazybot-3.webp"]}
url="https://lazybot.arthurp.fr"
githubUrl="https://github.com/arthur-pbty/LazyBot"
/>
);
}
+1
View File
@@ -43,6 +43,7 @@ export default function LearnInfo() {
]}
images={["/placeholder-learn.webp", "/placeholder-learn-2.webp", "/placeholder-learn-3.webp"]}
url="https://learn.arthurp.fr"
githubUrl="https://github.com/arthur-pbty/learn"
/>
);
}
+1
View File
@@ -44,6 +44,7 @@ export default function MoonInfo() {
]}
images={["/placeholder-moon.webp", "/placeholder-moon-2.webp", "/placeholder-moon-3.webp"]}
url="https://moon.arthurp.fr"
githubUrl="https://github.com/arthur-pbty/moon"
/>
);
}
+1
View File
@@ -43,6 +43,7 @@ export default function PomodoroInfo() {
]}
images={["/placeholder-pomodoro.webp", "/placeholder-pomodoro-2.webp", "/placeholder-pomodoro-3.webp"]}
url="https://pomodoro.arthurp.fr"
githubUrl="https://github.com/arthur-pbty/pomodoro"
/>
);
}
+1
View File
@@ -44,6 +44,7 @@ export default function PortfolioInfo() {
]}
images={["/placeholder-portfolio.webp", "/placeholder-portfolio-2.webp", "/placeholder-portfolio-3.webp"]}
url="https://portfolio.arthurp.fr"
githubUrl="https://github.com/arthur-pbty/portfolio"
/>
);
}
+1
View File
@@ -43,6 +43,7 @@ export default function QCUInfo() {
]}
images={["/placeholder-qcu.webp", "/placeholder-qcu-2.webp", "/placeholder-qcu-3.webp"]}
url="https://qcu.arthurp.fr"
githubUrl="https://github.com/arthur-pbty/QCM_physique"
/>
);
}
+1
View File
@@ -43,6 +43,7 @@ export default function QRCodeInfo() {
]}
images={["/placeholder-qrcode.webp", "/placeholder-qrcode-2.webp", "/placeholder-qrcode-3.webp"]}
url="https://qrcode.arthurp.fr"
githubUrl="https://github.com/arthur-pbty/qrcode"
/>
);
}
+1
View File
@@ -43,6 +43,7 @@ export default function Page() {
]}
images={["/placeholder-reducelink.webp", "/placeholder-reducelink-2.webp", "/placeholder-reducelink-3.webp"]}
url="https://reducelink.arthurp.fr/"
githubUrl="https://github.com/arthur-pbty/reducelink"
/>
);
}
+1
View File
@@ -43,6 +43,7 @@ export default function SudokuProjectPage() {
]}
images={["/placeholder-sudoku.webp", "/placeholder-sudoku-2.webp", "/placeholder-sudoku-3.webp"]}
url="https://sudoku.arthurp.fr"
githubUrl="https://github.com/arthur-pbty/sudoku"
/>
);
}
+1
View File
@@ -43,6 +43,7 @@ export default function VisioInfo() {
]}
images={["/placeholder-visio.webp", "/placeholder-visio-2.webp", "/placeholder-visio-3.webp"]}
url="https://visio.arthurp.fr"
githubUrl="https://github.com/arthur-pbty/visio"
/>
);
}
+51
View File
@@ -0,0 +1,51 @@
"use client";
import { useEffect, useState } from "react";
interface PastilleStatutProps {
url: string;
intervalMs?: number;
}
export default function PastilleStatut({ url, intervalMs = 60000 }: PastilleStatutProps) {
const [statut, setStatut] = useState<"en-ligne" | "hors-ligne" | "inconnu">("inconnu");
useEffect(() => {
let timeout: NodeJS.Timeout;
let isMounted = true;
const check = async () => {
try {
const res = await fetch(url, { method: "HEAD", mode: "no-cors" });
if (isMounted) setStatut("en-ligne");
} catch {
if (isMounted) setStatut("hors-ligne");
}
timeout = setTimeout(check, intervalMs);
};
check();
return () => {
isMounted = false;
clearTimeout(timeout);
};
}, [url, intervalMs]);
let color = "bg-gray-400", label = "Statut inconnu";
if (statut === "en-ligne") {
color = "bg-green-500";
label = "Site en ligne";
} else if (statut === "hors-ligne") {
color = "bg-red-500";
label = "Site hors ligne";
}
return (
<span
className={`inline-flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium text-white ${color}`}
title={label}
aria-label={label}
style={{ minWidth: 90 }}
>
<span className="w-2 h-2 rounded-full bg-white/80 mr-1" style={{ backgroundColor: color.replace("bg-", "") }} />
{label}
</span>
);
}
+25 -8
View File
@@ -1,6 +1,7 @@
import Image from "next/image";
import Link from "next/link";
import JsonLd from "@/components/JsonLd";
import PastilleStatut from "@/components/PastilleStatut";
interface FAQ {
question: string;
@@ -17,6 +18,7 @@ interface ProjectInfoPageProps {
faq?: FAQ[];
images: string[];
url: string;
githubUrl?: string;
}
export default function ProjectInfoPage({
@@ -29,6 +31,7 @@ export default function ProjectInfoPage({
faq,
images,
url,
githubUrl,
}: ProjectInfoPageProps) {
return (
<div className="min-h-screen bg-white font-sans text-zinc-900">
@@ -97,14 +100,28 @@ export default function ProjectInfoPage({
</div>
</div>
<a
href={url}
target="_blank"
rel="noopener noreferrer"
className="inline-block rounded-lg bg-[#e2d6c2] px-6 py-3 text-[#5a4a2e] font-medium hover:bg-[#d6bfa3] border border-[#d6bfa3] transition-colors"
>
{"Acc\u00e9der \u00e0 l\u2019outil"}
</a>
<div className="flex gap-4 mb-2 items-center">
<a
href={url}
target="_blank"
rel="noopener noreferrer"
className="inline-block rounded-lg bg-[#e2d6c2] px-6 py-3 text-[#5a4a2e] font-medium hover:bg-[#d6bfa3] border border-[#d6bfa3] transition-colors"
>
{"Acc\u00e9der \u00e0 l\u2019outil"}
</a>
{githubUrl && (
<a
href={githubUrl}
target="_blank"
rel="noopener noreferrer"
className="inline-block rounded-lg bg-zinc-900 px-6 py-3 text-white font-medium hover:bg-zinc-700 border border-zinc-800 transition-colors"
>
{"Voir sur GitHub"}
</a>
)}
<PastilleStatut url={url} />
</div>
{longDescription && (
<section className="mt-12">