- {competences.map((competence) => {
- const picture = competence.picture?.[0]; // Récupère la première image si elle existe
- const largeImageUrl = picture?.formats?.large?.url; // Vérifie que le format "large" existe
- const originalImageUrl = picture?.url; // URL de l'image originale
-
- // Utilisez l'URL de l'image originale si disponible, sinon l'URL de l'image large
- const imageUrl = originalImageUrl
- ? `http://localhost:1337${originalImageUrl}`
- : `http://localhost:1337${largeImageUrl}`;
-
- return (
-
-
-
- {imageUrl ? (
-
- ) : (
-
- Image indisponible
-
- )}
-
-
-
{competence.name}
-
{competence.description}
-
-
-
- );
- })}
-
-
- );
-}
-
-
+import Link from "next/link";
+
+// Fonction pour récupérer toutes les compétences depuis l'API Strapi
+async function getAllCompetences() {
+ try {
+ const response = await fetch("http://localhost:1337/api/competences?populate=*");
+ if (!response.ok) {
+ throw new Error("Failed to fetch competences");
+ }
+ const competences = await response.json();
+ return competences.data;
+ } catch (error) {
+ console.error("Error fetching competences:", error);
+ return [];
+ }
+}
+
+// Composant principal de la page des compétences
+export default async function Page() {
+ const competences = await getAllCompetences();
+
+ return (
+
+ {/* Titre de la page */}
+
Mes Compétences
+ {/* Grille pour afficher les compétences */}
+
+ {competences.map((competence) => {
+ const picture = competence.picture?.[0]; // Récupère la première image si elle existe
+ const largeImageUrl = picture?.formats?.large?.url; // Vérifie que le format "large" existe
+ const originalImageUrl = picture?.url; // URL de l'image originale
+
+ // Utilisez l'URL de l'image originale si disponible, sinon l'URL de l'image large
+ const imageUrl = originalImageUrl
+ ? `http://localhost:1337${originalImageUrl}`
+ : `http://localhost:1337${largeImageUrl}`;
+
+ return (
+
+ {/* Lien vers la page de détail de la compétence */}
+
+
+ {/* Affichage de l'image de la compétence */}
+ {imageUrl ? (
+
+ ) : (
+
+ Image indisponible
+
+ )}
+
+
+ {/* Affichage du nom de la compétence */}
+
{competence.name}
+ {/* Affichage de la description de la compétence */}
+
- );
- }
-
\ No newline at end of file
+// Composant principal de la page des messages
+export default async function MessagesPage() {
+ // Récupération des messages depuis l'API Strapi
+ const res = await fetch("http://localhost:1337/api/messages");
+ const { data } = await res.json();
+
+ return (
+
+ {/* Titre de la page */}
+
📬 Messages reçus
+
+ {/* Affichage d'un message si aucun message n'est reçu */}
+ {data.length === 0 ? (
+
Aucun message reçu.
+ ) : (
+
+ {/* Boucle sur les messages pour les afficher */}
+ {data.map((msg: any) => (
+
+ {/* Affichage du nom et de l'email de l'expéditeur */}
+
👤 {msg.name} ({msg.email})
+ {/* Affichage de la date de réception du message */}
+
-
- {/* 🟢 Modal plein écran inséré DANS `` grâce à `createPortal` */}
- {selectedImage &&
- createPortal(
-
setSelectedImage(null)} // 🔴 Fermer au clic
- >
-
- {/* Bouton de fermeture */}
-
-
- {/* Image affichée en grand */}
-
-
-
,
- document.body // 🟢 Place le `modal` en dehors de `` dans ``
- )}
- >
- );
-}
+"use client";
+
+import { useState } from "react";
+import { createPortal } from "react-dom"; // Importation de createPortal pour les modals
+import { Swiper, SwiperSlide } from "swiper/react";
+import { Navigation, Pagination, Autoplay } from "swiper/modules";
+import "swiper/css";
+import "swiper/css/navigation";
+import "swiper/css/pagination";
+
+interface CarouselProps {
+ images: Array<{ url: string; alt: string }>; // Propriétés des images du carrousel
+ className?: string; // Classe CSS optionnelle pour personnaliser le style
+}
+
+export default function Carousel({ images, className }: CarouselProps) {
+ const [selectedImage, setSelectedImage] = useState(null); // État pour l'image sélectionnée
+
+ return (
+ <>
+ {/* Carrousel principal */}
+
+
+ {/* Boucle sur les images pour les afficher dans le carrousel */}
+ {images.map((img, index) => (
+
+ {/* Image cliquable pour affichage en plein écran */}
+ setSelectedImage(img.url)} // Ouvre l’image en plein écran
+ />
+
+ ))}
+
+
+
+ {/* Modal plein écran inséré dans grâce à createPortal */}
+ {selectedImage &&
+ createPortal(
+
setSelectedImage(null)} // Fermer au clic
+ >
+
+ {/* Bouton de fermeture */}
+
+
+ {/* Image affichée en grand */}
+
+
-
- {/* Modal plein écran pour agrandir les images */}
- {selectedImage &&
- createPortal(
-
setSelectedImage(null)}
- >
-
-
-
-
-
,
- document.body
- )}
- >
- );
-}
+"use client";
+
+import { useState } from "react";
+import { createPortal } from "react-dom"; // Importation de createPortal pour les modals
+import { Swiper, SwiperSlide } from "swiper/react";
+import { Navigation, Pagination, Autoplay } from "swiper/modules";
+import "swiper/css";
+import "swiper/css/navigation";
+import "swiper/css/pagination";
+
+interface CarouselProps {
+ images: Array<{ url: string; alt: string }>; // Propriétés des images du carrousel
+ className?: string; // Classe CSS optionnelle pour personnaliser le style
+}
+
+export default function CarouselCompetences({ images, className }: CarouselProps) {
+ const [selectedImage, setSelectedImage] = useState(null); // État pour l'image sélectionnée
+
+ return (
+ <>
+ {/* Carrousel compétences */}
+
+
+ {/* Boucle sur les images pour les afficher dans le carrousel */}
+ {images.map((img, index) => (
+
+ {/* Image cliquable pour affichage en plein écran */}
+ setSelectedImage(img.url)} // Ouvre l’image en plein écran
+ />
+
+ ))}
+
+
+
+ {/* Modal plein écran pour agrandir les images */}
+ {selectedImage &&
+ createPortal(
+
setSelectedImage(null)} // Fermer au clic
+ >
+
+ {/* Bouton de fermeture */}
+
+
+ {/* Image affichée en grand */}
+
+
+
,
+ document.body
+ )}
+ >
+ );
+}
\ No newline at end of file
diff --git a/app/components/ContactForm.tsx b/app/components/ContactForm.tsx
index a768349..18d4aed 100644
--- a/app/components/ContactForm.tsx
+++ b/app/components/ContactForm.tsx
@@ -1,102 +1,103 @@
-"use client";
-
-import { useState } from "react";
-import { sendMessage } from "../utils/sendMessage";
-
-export default function ContactForm() {
- const [name, setName] = useState("");
- const [email, setEmail] = useState("");
- const [message, setMessage] = useState("");
- const [status, setStatus] = useState("");
- const [isSuccess, setIsSuccess] = useState(null);
- const [isLoading, setIsLoading] = useState(false); // ✅ Nouvel état pour désactiver le bouton
-
- const handleSubmit = async (e: React.FormEvent) => {
- e.preventDefault();
-
- if (!name.trim() || !email.trim() || !message.trim()) {
- setStatus("❌ Tous les champs sont obligatoires.");
- setIsSuccess(false);
- return;
- }
-
- if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
- setStatus("❌ Email invalide.");
- setIsSuccess(false);
- return;
- }
-
- setStatus("⏳ Envoi en cours...");
- setIsSuccess(null);
- setIsLoading(true); // ✅ Désactive le bouton pendant l'envoi
-
- try {
- await sendMessage(name, email, message);
- setStatus("✅ Message envoyé avec succès !");
- setIsSuccess(true);
- setName("");
- setEmail("");
- setMessage("");
- } catch (error) {
- setStatus("❌ Erreur lors de l'envoi du message.");
- setIsSuccess(false);
- } finally {
- setIsLoading(false); // ✅ Réactive le bouton après l'envoi
- }
- };
-
- return (
-
- );
-}
+"use client";
+
+import { useState } from "react";
+import { sendMessage } from "../utils/sendMessage";
+
+export default function ContactForm() {
+ // États pour gérer les valeurs des champs de formulaire
+ const [name, setName] = useState("");
+ const [email, setEmail] = useState("");
+ const [message, setMessage] = useState("");
+ const [status, setStatus] = useState("");
+ const [isSuccess, setIsSuccess] = useState(null);
+ const [isLoading, setIsLoading] = useState(false); // ✅ Nouvel état pour désactiver le bouton
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+
+ if (!name.trim() || !email.trim() || !message.trim()) {
+ setStatus("❌ Tous les champs sont obligatoires.");
+ setIsSuccess(false);
+ return;
+ }
+
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
+ setStatus("❌ Email invalide.");
+ setIsSuccess(false);
+ return;
+ }
+
+ setStatus("⏳ Envoi en cours...");
+ setIsSuccess(null);
+ setIsLoading(true); // ✅ Désactive le bouton pendant l'envoi
+
+ try {
+ await sendMessage(name, email, message);
+ setStatus("✅ Message envoyé avec succès !");
+ setIsSuccess(true);
+ setName("");
+ setEmail("");
+ setMessage("");
+ } catch (error) {
+ setStatus("❌ Erreur lors de l'envoi du message.");
+ setIsSuccess(false);
+ } finally {
+ setIsLoading(false); // ✅ Réactive le bouton après l'envoi
+ }
+ };
+
+ return (
+
+ );
+}
diff --git a/app/components/ContentSection.tsx b/app/components/ContentSection.tsx
index a016dd4..9a2a3d5 100644
--- a/app/components/ContentSection.tsx
+++ b/app/components/ContentSection.tsx
@@ -1,54 +1,60 @@
-import { fetchData } from "../utils/fetchData";
-import Carousel from "./Carousel";
-import ReactMarkdown from "react-markdown";
-
-interface ContentSectionProps {
- collection: string; // Nom de la collection (projects, events, blog, etc.)
- slug: string;
- titleClass?: string; // Permet de modifier le style du titre
- contentClass?: string; // Permet de modifier le style du contenu
-}
-
-export default async function ContentSection({ collection, slug, titleClass, contentClass }: ContentSectionProps) {
- const data = await fetchData(collection, slug);
-
- if (!data) {
- return
Contenu introuvable.
;
- }
-
- const { name, Resum: richText, picture, link, linkText } = data;
-
- // Transformer les images de Strapi en format attendu par le carrousel
- const images = picture?.map((img: any) => ({
- url: `http://localhost:1337${img?.formats?.large?.url || img?.url}`,
- alt: img.name || "Image",
- })) || [];
-
- return (
-
- );
+import { fetchData } from "../utils/fetchData"; // Importation de la fonction fetchData pour récupérer les données depuis l'API
+import Carousel from "./Carousel"; // Importation du composant Carousel pour afficher les images
+import ReactMarkdown from "react-markdown"; // Importation de ReactMarkdown pour rendre le texte riche en Markdown
+
+// Définition des propriétés du composant ContentSection
+interface ContentSectionProps {
+ collection: string; // Nom de la collection (projects, events, blog, etc.)
+ slug: string; // Identifiant unique pour récupérer les données spécifiques
+ titleClass?: string; // Permet de modifier le style du titre
+ contentClass?: string; // Permet de modifier le style du contenu
+}
+
+// Composant principal ContentSection
+export default async function ContentSection({ collection, slug, titleClass, contentClass }: ContentSectionProps) {
+ // Récupération des données depuis l'API en utilisant la fonction fetchData
+ const data = await fetchData(collection, slug);
+
+ // Affichage d'un message si les données ne sont pas disponibles
+ if (!data) {
+ return
Contenu introuvable.
;
+ }
+
+ // Déstructuration des données récupérées
+ const { name, Resum: richText, picture, link, linkText } = data;
+
+ // Transformation des images de Strapi en format attendu par le carrousel
+ const images = picture?.map((img: any) => ({
+ url: `http://localhost:1337${img?.formats?.large?.url || img?.url}`, // Utilisation de l'URL de l'image en format large ou originale
+ alt: img.name || "Image", // Texte alternatif pour l'image
+ })) || [];
+
+ return (
+
+ {/* Titre de la section */}
+
{name}
+
+ {/* Carrousel réutilisable pour afficher les images */}
+
+
+ {/* Contenu en Markdown */}
+
+ );
+}
\ No newline at end of file
diff --git a/app/globals.css b/app/globals.css
index d32d16a..875bf95 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -1,37 +1,42 @@
+/* Importation des styles de base, des composants et des utilitaires de Tailwind CSS */
@tailwind base;
@tailwind components;
@tailwind utilities;
+/* Définition des variables CSS pour les couleurs de fond et de premier plan */
:root {
- --background: #ffffff;
- --foreground: #171717;
+ --background: #ffffff; /* Couleur de fond par défaut (clair) */
+ --foreground: #171717; /* Couleur de premier plan par défaut (foncé) */
}
+/* Définition des variables CSS pour le mode sombre */
@media (prefers-color-scheme: dark) {
:root {
- --background: #0a0a0a;
- --foreground: #ededed;
+ --background: #0a0a0a; /* Couleur de fond pour le mode sombre */
+ --foreground: #ededed; /* Couleur de premier plan pour le mode sombre */
}
}
+/* Styles globaux pour le corps de la page */
body {
- color: var(--foreground);
- background: var(--background);
- font-family: Arial, Helvetica, sans-serif;
+ color: var(--foreground); /* Utilisation de la couleur de premier plan définie */
+ background: var(--background); /* Utilisation de la couleur de fond définie */
+ font-family: Arial, Helvetica, sans-serif; /* Police de caractères par défaut */
}
+/* Définition d'une animation de fondu en entrée */
@keyframes fade-in {
from {
- opacity: 0;
- transform: translateY(-10px);
+ opacity: 0; /* Opacité initiale à 0 (invisible) */
+ transform: translateY(-10px); /* Déplacement initial vers le haut */
}
to {
- opacity: 1;
- transform: translateY(0);
+ opacity: 1; /* Opacité finale à 1 (visible) */
+ transform: translateY(0); /* Position finale */
}
}
+/* Classe utilitaire pour appliquer l'animation de fondu en entrée */
.animate-fade-in {
- animation: fade-in 0.5s ease-out;
-}
-
+ animation: fade-in 0.5s ease-out; /* Animation de 0.5s avec une courbe de transition */
+}
\ No newline at end of file
diff --git a/app/layout.tsx b/app/layout.tsx
index df338e2..d41d449 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -6,16 +6,17 @@ import "./assets/main.css";
import NavLink from "./components/NavLink";
export default function RootLayout({ children }) {
+ // États pour gérer la largeur et la hauteur du conteneur principal
const [numElements, setNumElements] = useState(0);
const [containerWidth, setContainerWidth] = useState("max-w-4xl");
const [containerHeight, setContainerHeight] = useState("min-h-[50vh]");
useEffect(() => {
- // Supposons que children soit un tableau d'éléments
+ // Compter le nombre d'éléments enfants
const elementsCount = React.Children.count(children);
setNumElements(elementsCount);
- // Ajustez la largeur en fonction du nombre d'éléments
+ // Ajuster la largeur et la hauteur en fonction du nombre d'éléments
if (elementsCount > 5) {
setContainerWidth("max-w-6xl");
setContainerHeight("min-h-[80vh]");
@@ -31,11 +32,14 @@ export default function RootLayout({ children }) {
return (
+ {/* Conteneur principal avec image de fond */}
+ {/* Cercles de fond pour l'effet visuel */}
+ {/* En-tête avec navigation */}
Portofolio Gras-Calvet Fernand
@@ -57,9 +61,11 @@ export default function RootLayout({ children }) {
+ {/* Conteneur principal pour le contenu */}
{children}
+ {/* Pied de page */}
diff --git a/app/portfolio/[slug]/page.tsx b/app/portfolio/[slug]/page.tsx
index f871f14..46cef79 100644
--- a/app/portfolio/[slug]/page.tsx
+++ b/app/portfolio/[slug]/page.tsx
@@ -1,5 +1,7 @@
-import ContentSection from "../../components/ContentSection";
-
-export default function Page({ params }: { params: { slug: string } }) {
- return ;
-}
+import ContentSection from "../../components/ContentSection"; // Importation du composant ContentSection
+
+// Composant principal de la page de détail du projet
+export default function Page({ params }: { params: { slug: string } }) {
+ // Rendu du composant ContentSection avec les paramètres de la collection et du slug
+ return ;
+}
\ No newline at end of file
diff --git a/app/portfolio/page.jsx b/app/portfolio/page.jsx
index 2feea83..2f61216 100644
--- a/app/portfolio/page.jsx
+++ b/app/portfolio/page.jsx
@@ -1,59 +1,69 @@
-import Link from "next/link";
-
-async function getAllprojects() {
- try {
- const response = await fetch("http://localhost:1337/api/projects?populate=*");
- if (!response.ok) {
- throw new Error("Failed to fetch projects");
- }
- const projects = await response.json();
- return projects.data;
- } catch (error) {
- console.error("Error fetching projects:", error);
- return [];
- }
-}
-
-export default async function Page() {
- const projects = await getAllprojects();
-
- return (
-
-
Portfolio formation 42
-
- {projects.map((project) => {
- const picture = project.picture?.[0]; // Récupère la première image si elle existe
- const largeImageUrl = picture?.formats?.large?.url; // Vérifie que le format "large" existe
- const originalImageUrl = picture?.url; // URL de l'image originale
-
- // Utilisez l'URL de l'image originale si disponible, sinon l'URL de l'image large
- const imageUrl = originalImageUrl ? `http://localhost:1337${originalImageUrl}` : `http://localhost:1337${largeImageUrl}`;
-
- return (
-
-
-
- {imageUrl ? (
-
- ) : (
-
- Image indisponible
-
- )}
-
-
-
{project.name}
-
{project.description}
-
-
-
- );
- })}
-
-
- );
+import Link from "next/link";
+
+// Fonction pour récupérer tous les projets depuis l'API Strapi
+async function getAllprojects() {
+ try {
+ const response = await fetch("http://localhost:1337/api/projects?populate=*");
+ if (!response.ok) {
+ throw new Error("Failed to fetch projects");
+ }
+ const projects = await response.json();
+ return projects.data;
+ } catch (error) {
+ console.error("Error fetching projects:", error);
+ return [];
+ }
+}
+
+// Composant principal de la page des projets
+export default async function Page() {
+ const projects = await getAllprojects();
+
+ return (
+
+ {/* Titre de la page */}
+
Portfolio formation 42
+
+ {/* Grille pour afficher les projets */}
+
+ {/* Boucle sur les projets pour les afficher */}
+ {projects.map((project) => {
+ const picture = project.picture?.[0]; // Récupère la première image si elle existe
+ const largeImageUrl = picture?.formats?.large?.url; // Vérifie que le format "large" existe
+ const originalImageUrl = picture?.url; // URL de l'image originale
+
+ // Utilisez l'URL de l'image originale si disponible, sinon l'URL de l'image large
+ const imageUrl = originalImageUrl ? `http://localhost:1337${originalImageUrl}` : `http://localhost:1337${largeImageUrl}`;
+
+ return (
+
+ {/* Lien vers la page de détail du projet */}
+
+
+ {/* Affichage de l'image du projet */}
+ {imageUrl ? (
+
+ ) : (
+
+ Image indisponible
+
+ )}
+
+
+ {/* Affichage du nom du projet */}
+
{project.name}
+ {/* Affichage de la description du projet */}
+
{project.description}
+
+
+
+ );
+ })}
+
+
+ );
}
\ No newline at end of file
diff --git a/app/utils/fetchData.ts b/app/utils/fetchData.ts
index 8d314e5..36b8026 100644
--- a/app/utils/fetchData.ts
+++ b/app/utils/fetchData.ts
@@ -1,24 +1,30 @@
-import qs from "qs";
-
-export async function fetchData(collection: string, slug: string) {
- const query = qs.stringify({
- filters: { slug },
- populate: "picture",
- });
-
- try {
- const response = await fetch(`http://localhost:1337/api/${collection}?${query}`, {
- cache: "no-store",
- });
-
- if (!response.ok) {
- throw new Error("Failed to fetch data");
- }
-
- const data = await response.json();
- return data.data[0] || null;
- } catch (error) {
- console.error(`Error fetching ${collection} data:`, error);
- return null;
- }
-}
+import qs from "qs"; // Importation de qs pour construire des requêtes de chaîne de requête
+
+// Fonction pour récupérer des données spécifiques depuis l'API Strapi
+export async function fetchData(collection: string, slug: string) {
+ // Construction de la requête avec des filtres et des relations à peupler
+ const query = qs.stringify({
+ filters: { slug }, // Filtre basé sur le slug
+ populate: "picture", // On garde les images associées
+ });
+
+ try {
+ // Envoi de la requête à l'API Strapi
+ const response = await fetch(`http://localhost:1337/api/${collection}?${query}`, {
+ cache: "no-store", // Désactivation du cache pour obtenir les données les plus récentes
+ });
+
+ // Vérification de la réponse de l'API
+ if (!response.ok) {
+ throw new Error("Failed to fetch data");
+ }
+
+ // Récupération des données de la réponse
+ const data = await response.json();
+ return data.data[0] || null; // Retourne la première entrée ou null si aucune donnée n'est trouvée
+ } catch (error) {
+ // Gestion des erreurs et log des erreurs
+ console.error(`Error fetching ${collection} data:`, error);
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/app/utils/fetchDataCompetences.ts b/app/utils/fetchDataCompetences.ts
index 2cc6e77..1fa7924 100644
--- a/app/utils/fetchDataCompetences.ts
+++ b/app/utils/fetchDataCompetences.ts
@@ -1,54 +1,67 @@
-import qs from "qs";
-
-// ✅ Fonction pour récupérer une compétence spécifique
-export async function fetchDataCompetences(collection: string, slug: string) {
- const query = qs.stringify({
- filters: {
- slug: { $eq: slug },
- },
- populate: "picture", // On garde les images des compétences
- });
-
- console.log(`🛠️ Requête API Compétence : http://localhost:1337/api/${collection}?${query}`);
-
- try {
- const response = await fetch(`http://localhost:1337/api/${collection}?${query}`, {
- cache: "no-store",
- });
-
- if (!response.ok) {
- throw new Error(`Failed to fetch competences data: ${response.status}`);
- }
-
- const data = await response.json();
- console.log("✅ Données reçues (Compétence) :", data);
-
- return data.data[0] || null;
- } catch (error) {
- console.error("❌ Erreur lors de la récupération des compétences :", error);
- return null;
- }
-}
-
-export async function fetchDataGlossaire() {
- try {
- console.log("🛠️ Requête API Glossaire : http://localhost:1337/api/glossaires?populate=images");
-
- const response = await fetch("http://localhost:1337/api/glossaires?populate=images", {
- cache: "no-store",
- });
-
- if (!response.ok) {
- throw new Error(`Failed to fetch glossaire data: ${response.status}`);
- }
-
- const data = await response.json();
- console.log("✅ Données reçues (Glossaire) :", data);
-
- return data.data || [];
- } catch (error) {
- console.error("❌ Erreur lors de la récupération du glossaire :", error);
- return [];
- }
-}
-
+import qs from "qs"; // Importation de qs pour construire des requêtes de chaîne de requête
+
+// Fonction pour récupérer une compétence spécifique
+export async function fetchDataCompetences(collection: string, slug: string) {
+ // Construction de la requête avec des filtres et des relations à peupler
+ const query = qs.stringify({
+ filters: {
+ slug: { $eq: slug },
+ },
+ populate: "picture", // On garde les images des compétences
+ });
+
+ // Log de la requête API pour le débogage
+ console.log(`🛠️ Requête API Compétence : http://localhost:1337/api/${collection}?${query}`);
+
+ try {
+ // Envoi de la requête à l'API Strapi
+ const response = await fetch(`http://localhost:1337/api/${collection}?${query}`, {
+ cache: "no-store",
+ });
+
+ // Vérification de la réponse de l'API
+ if (!response.ok) {
+ throw new Error(`Failed to fetch competences data: ${response.status}`);
+ }
+
+ // Récupération des données de la réponse
+ const data = await response.json();
+ console.log("✅ Données reçues (Compétence) :", data);
+
+ // Retourne la première compétence ou null si aucune donnée n'est trouvée
+ return data.data[0] || null;
+ } catch (error) {
+ // Gestion des erreurs et log des erreurs
+ console.error("❌ Erreur lors de la récupération des compétences :", error);
+ return null;
+ }
+}
+
+// Fonction pour récupérer les données du glossaire
+export async function fetchDataGlossaire() {
+ try {
+ // Log de la requête API pour le débogage
+ console.log("🛠️ Requête API Glossaire : http://localhost:1337/api/glossaires?populate=images");
+
+ // Envoi de la requête à l'API Strapi
+ const response = await fetch("http://localhost:1337/api/glossaires?populate=images", {
+ cache: "no-store",
+ });
+
+ // Vérification de la réponse de l'API
+ if (!response.ok) {
+ throw new Error(`Failed to fetch glossaire data: ${response.status}`);
+ }
+
+ // Récupération des données de la réponse
+ const data = await response.json();
+ console.log("✅ Données reçues (Glossaire) :", data);
+
+ // Retourne les données du glossaire ou un tableau vide si aucune donnée n'est trouvée
+ return data.data || [];
+ } catch (error) {
+ // Gestion des erreurs et log des erreurs
+ console.error("❌ Erreur lors de la récupération du glossaire :", error);
+ return [];
+ }
+}
\ No newline at end of file
diff --git a/app/utils/sendMessage.ts b/app/utils/sendMessage.ts
index 1c1ed7d..647933c 100644
--- a/app/utils/sendMessage.ts
+++ b/app/utils/sendMessage.ts
@@ -1,32 +1,39 @@
-export async function sendMessage(name: string, email: string, message: string) {
- const dateTime = new Date().toLocaleString("fr-FR", { timeZone: "Europe/Paris" }); // ✅ Date formatée en français
-
- const messageWithDate = `${message}\n\n📅 Envoyé le : ${dateTime}`; // ✅ Ajout de la date à la fin du message
-
- console.log("📨 Envoi du message...", { name, email, messageWithDate });
-
- const res = await fetch("http://localhost:1337/api/messages", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- data: {
- name: name,
- email: email,
- message: messageWithDate, // ✅ Message modifié avec la date
- },
- }),
- });
-
- const responseData = await res.json();
-
- if (!res.ok) {
- console.error("❌ Erreur API Strapi :", responseData);
- throw new Error(`Échec de l'envoi du message: ${responseData.error.message}`);
- }
-
- console.log("✅ Message envoyé avec succès !", responseData);
- return responseData;
- }
-
\ No newline at end of file
+// Fonction pour envoyer un message à l'API Strapi
+export async function sendMessage(name: string, email: string, message: string) {
+ // Formatage de la date et de l'heure en français
+ const dateTime = new Date().toLocaleString("fr-FR", { timeZone: "Europe/Paris" }); // ✅ Date formatée en français
+
+ // Ajout de la date à la fin du message
+ const messageWithDate = `${message}\n\n📅 Envoyé le : ${dateTime}`; // ✅ Ajout de la date à la fin du message
+
+ // Log des informations du message avant l'envoi
+ console.log("📨 Envoi du message...", { name, email, messageWithDate });
+
+ // Envoi du message à l'API Strapi
+ const res = await fetch("http://localhost:1337/api/messages", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ data: {
+ name: name,
+ email: email,
+ message: messageWithDate, // ✅ Message modifié avec la date
+ },
+ }),
+ });
+
+ // Récupération de la réponse de l'API
+ const responseData = await res.json();
+
+ // Gestion des erreurs de l'API
+ if (!res.ok) {
+ console.error("❌ Erreur API Strapi :", responseData);
+ throw new Error(`Échec de l'envoi du message: ${responseData.error.message}`);
+ }
+
+ // Log de la réussite de l'envoi du message
+ console.log("✅ Message envoyé avec succès !", responseData);
+ return responseData;
+}
\ No newline at end of file