first update
This commit is contained in:
@@ -0,0 +1,365 @@
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
AppBar,
|
||||
Toolbar,
|
||||
Typography,
|
||||
Button,
|
||||
Box,
|
||||
Drawer,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemText,
|
||||
TextField,
|
||||
IconButton,
|
||||
Divider,
|
||||
Menu,
|
||||
MenuItem,
|
||||
CircularProgress,
|
||||
} from "@mui/material";
|
||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||
import MenuIcon from "@mui/icons-material/Menu";
|
||||
import SearchIcon from "@mui/icons-material/Search";
|
||||
import HomeIcon from "@mui/icons-material/Home";
|
||||
import ArticleIcon from "@mui/icons-material/Article";
|
||||
import InfoIcon from "@mui/icons-material/Info";
|
||||
import ContactMailIcon from "@mui/icons-material/ContactMail";
|
||||
import WorkIcon from "@mui/icons-material/Work";
|
||||
|
||||
const Header = () => {
|
||||
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [isSearching, setIsSearching] = useState(false);
|
||||
const [anchorEl, setAnchorEl] = useState(null); // Pour gérer le menu Services
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const toggleDrawer = (open) => (event) => {
|
||||
if (
|
||||
event.type === "keydown" &&
|
||||
(event.key === "Tab" || event.key === "Shift")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
setDrawerOpen(open);
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
if (searchQuery.trim()) {
|
||||
setIsSearching(true);
|
||||
setTimeout(() => {
|
||||
navigate(`/search?query=${encodeURIComponent(searchQuery.trim())}`);
|
||||
setIsSearching(false);
|
||||
}, 500);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMenuOpen = (event) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleMenuClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const commonButtonStyles = {
|
||||
transition: "color 0.3s",
|
||||
position: "relative",
|
||||
"&:hover": { color: "#00bcd4" },
|
||||
"&:after": {
|
||||
content: '""',
|
||||
position: "absolute",
|
||||
width: "0",
|
||||
height: "2px",
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
backgroundColor: "#00bcd4",
|
||||
transition: "width 0.3s",
|
||||
},
|
||||
"&:hover:after": {
|
||||
width: "100%",
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<AppBar
|
||||
position="static"
|
||||
color="transparent"
|
||||
elevation={0}
|
||||
sx={{
|
||||
background: "linear-gradient(to right, #0e467f, #00bcd4)",
|
||||
color: "white",
|
||||
transition: "all 0.3s ease",
|
||||
}}
|
||||
>
|
||||
<Toolbar>
|
||||
{/* Mobile menu icon */}
|
||||
<IconButton
|
||||
edge="start"
|
||||
color="inherit"
|
||||
aria-label="menu"
|
||||
onClick={toggleDrawer(true)}
|
||||
sx={{ display: { xs: "block", md: "none" } }}
|
||||
>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
|
||||
{/* Logo as Home Link */}
|
||||
<Typography
|
||||
variant="h6"
|
||||
component={Link}
|
||||
to="/"
|
||||
sx={{
|
||||
flexGrow: 1,
|
||||
textAlign: { xs: "center", md: "left" },
|
||||
fontWeight: "bold",
|
||||
textDecoration: "none",
|
||||
color: "white",
|
||||
"&:hover": {
|
||||
color: "#00bcd4",
|
||||
},
|
||||
}}
|
||||
>
|
||||
Mon Site React
|
||||
</Typography>
|
||||
|
||||
{/* Navigation Links */}
|
||||
<Box sx={{ display: { xs: "none", md: "flex" }, gap: 2 }}>
|
||||
<Button
|
||||
component={Link}
|
||||
to="/posts"
|
||||
color="inherit"
|
||||
sx={{
|
||||
fontWeight: location.pathname === "/posts" ? "bold" : "normal",
|
||||
textDecoration:
|
||||
location.pathname === "/posts" ? "underline" : "none",
|
||||
...commonButtonStyles,
|
||||
}}
|
||||
>
|
||||
Articles
|
||||
</Button>
|
||||
|
||||
{/* Services Dropdown */}
|
||||
<Button
|
||||
color="inherit"
|
||||
aria-controls="services-menu"
|
||||
aria-haspopup="true"
|
||||
onClick={handleMenuOpen}
|
||||
sx={{
|
||||
fontWeight: location.pathname.includes("/services")
|
||||
? "bold"
|
||||
: "normal",
|
||||
textDecoration: location.pathname.includes("/services")
|
||||
? "underline"
|
||||
: "none",
|
||||
...commonButtonStyles,
|
||||
}}
|
||||
>
|
||||
Services
|
||||
</Button>
|
||||
<Menu
|
||||
id="services-menu"
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={handleMenuClose}
|
||||
MenuListProps={{
|
||||
"aria-labelledby": "services-button",
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
component={Link}
|
||||
to="/services/formation-web"
|
||||
onClick={handleMenuClose}
|
||||
>
|
||||
<WorkIcon sx={{ marginRight: 1 }} />
|
||||
Formation Web
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
component={Link}
|
||||
to="/services/formation-ia"
|
||||
onClick={handleMenuClose}
|
||||
>
|
||||
<WorkIcon sx={{ marginRight: 1 }} />
|
||||
Formation IA
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
component={Link}
|
||||
to="/services/formation-video"
|
||||
onClick={handleMenuClose}
|
||||
>
|
||||
<WorkIcon sx={{ marginRight: 1 }} />
|
||||
Formation Video
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
|
||||
<Button
|
||||
component={Link}
|
||||
to="/about"
|
||||
color="inherit"
|
||||
sx={{
|
||||
fontWeight: location.pathname === "/about" ? "bold" : "normal",
|
||||
textDecoration:
|
||||
location.pathname === "/about" ? "underline" : "none",
|
||||
...commonButtonStyles,
|
||||
}}
|
||||
>
|
||||
À propos
|
||||
</Button>
|
||||
<Button
|
||||
component={Link}
|
||||
to="/contact"
|
||||
color="inherit"
|
||||
sx={{
|
||||
fontWeight: location.pathname === "/contact" ? "bold" : "normal",
|
||||
textDecoration:
|
||||
location.pathname === "/contact" ? "underline" : "none",
|
||||
...commonButtonStyles,
|
||||
}}
|
||||
>
|
||||
Contact
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
{/* Search Field */}
|
||||
<Box
|
||||
component="form"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
handleSearch();
|
||||
}}
|
||||
sx={{
|
||||
display: { xs: "none", md: "flex" },
|
||||
alignItems: "center",
|
||||
gap: 1,
|
||||
}}
|
||||
>
|
||||
<label
|
||||
htmlFor="search-input"
|
||||
style={{ position: "absolute", left: "-9999px" }}
|
||||
>
|
||||
Rechercher sur le site
|
||||
</label>
|
||||
<TextField
|
||||
id="search-input"
|
||||
variant="outlined"
|
||||
size="small"
|
||||
placeholder="Rechercher..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
sx={{
|
||||
backgroundColor: "rgba(255, 255, 255, 0.9)",
|
||||
borderRadius: 1,
|
||||
"& .MuiOutlinedInput-root": {
|
||||
"& fieldset": {
|
||||
borderColor: "white",
|
||||
},
|
||||
"&:hover fieldset": {
|
||||
borderColor: "#00bcd4",
|
||||
},
|
||||
"&.Mui-focused fieldset": {
|
||||
borderColor: "#00bcd4",
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<IconButton type="submit" color="inherit" aria-label="Rechercher">
|
||||
{isSearching ? (
|
||||
<CircularProgress size={20} color="inherit" />
|
||||
) : (
|
||||
<SearchIcon />
|
||||
)}
|
||||
</IconButton>
|
||||
</Box>
|
||||
|
||||
{/* Drawer for mobile */}
|
||||
<Drawer anchor="left" open={drawerOpen} onClose={toggleDrawer(false)}>
|
||||
<Box
|
||||
sx={{
|
||||
width: 250,
|
||||
backgroundColor: "#f5f5f5",
|
||||
height: "100%",
|
||||
padding: 2,
|
||||
}}
|
||||
role="presentation"
|
||||
onClick={toggleDrawer(false)}
|
||||
onKeyDown={toggleDrawer(false)}
|
||||
>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{ textAlign: "center", marginBottom: 2 }}
|
||||
>
|
||||
Menu
|
||||
</Typography>
|
||||
<Divider />
|
||||
<List>
|
||||
<ListItem
|
||||
button
|
||||
component={Link}
|
||||
to="/"
|
||||
selected={location.pathname === "/"}
|
||||
>
|
||||
<HomeIcon sx={{ marginRight: 1 }} />
|
||||
<ListItemText primary="Accueil" />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
button
|
||||
component={Link}
|
||||
to="/posts"
|
||||
selected={location.pathname === "/posts"}
|
||||
>
|
||||
<ArticleIcon sx={{ marginRight: 1 }} />
|
||||
<ListItemText primary="Articles" />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
button
|
||||
component={Link}
|
||||
to="/services/formation-web"
|
||||
selected={location.pathname.includes("/services/formation-web")}
|
||||
>
|
||||
<WorkIcon sx={{ marginRight: 1 }} />
|
||||
<ListItemText primary="Formation Web" />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
button
|
||||
component={Link}
|
||||
to="/services/formation-ia"
|
||||
selected={location.pathname.includes("/services/formation-ia")}
|
||||
>
|
||||
<WorkIcon sx={{ marginRight: 1 }} />
|
||||
<ListItemText primary="Formation IA" />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
button
|
||||
component={Link}
|
||||
to="/services/formation-video"
|
||||
selected={location.pathname.includes("/services/formation-video")}
|
||||
>
|
||||
<WorkIcon sx={{ marginRight: 1 }} />
|
||||
<ListItemText primary="Formation Video" />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
button
|
||||
component={Link}
|
||||
to="/about"
|
||||
selected={location.pathname === "/about"}
|
||||
>
|
||||
<InfoIcon sx={{ marginRight: 1 }} />
|
||||
<ListItemText primary="À propos" />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
button
|
||||
component={Link}
|
||||
to="/contact"
|
||||
selected={location.pathname === "/contact"}
|
||||
>
|
||||
<ContactMailIcon sx={{ marginRight: 1 }} />
|
||||
<ListItemText primary="Contact" />
|
||||
</ListItem>
|
||||
</List>
|
||||
</Box>
|
||||
</Drawer>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
@@ -0,0 +1,71 @@
|
||||
import React from 'react';
|
||||
import { Box, Typography, Button } from '@mui/material';
|
||||
|
||||
const Hero = ({ backgroundImage, useGradient, title, titleColor, text, textColor }) => {
|
||||
// Priorité pour appliquer le fond : Image > Dégradé > Aucun fond
|
||||
const backgroundStyle = backgroundImage
|
||||
? `url(${backgroundImage}) center/cover no-repeat`
|
||||
: useGradient
|
||||
? 'linear-gradient(to bottom, #0e467f, rgb(9, 48, 87))'
|
||||
: '#0e467f'; // Fallback couleur
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: { xs: '50vh', md: '70vh', lg: '80vh' }, // Responsive
|
||||
background: backgroundStyle,
|
||||
color: 'white',
|
||||
textAlign: 'center',
|
||||
padding: '20px',
|
||||
transition: 'background 0.5s ease-in-out', // Transition fluide pour le fond
|
||||
}}
|
||||
>
|
||||
{/* Titre */}
|
||||
<Typography
|
||||
variant="h1"
|
||||
sx={{
|
||||
fontWeight: 'bold',
|
||||
mb: 2,
|
||||
color: titleColor || '#ffffff', // Couleur par défaut
|
||||
fontSize: { xs: '2rem', md: '3rem', lg: '4rem' }, // Responsive
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</Typography>
|
||||
|
||||
{/* Texte */}
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{
|
||||
mb: 4,
|
||||
color: textColor || '#ffffff', // Couleur par défaut
|
||||
fontSize: { xs: '1rem', md: '1.2rem', lg: '1.5rem' }, // Responsive
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</Typography>
|
||||
|
||||
{/* Bouton */}
|
||||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
size="large"
|
||||
href="/posts"
|
||||
sx={{
|
||||
textTransform: 'none',
|
||||
fontWeight: 'bold',
|
||||
padding: { xs: '10px 20px', md: '15px 30px' }, // Responsive
|
||||
fontSize: { xs: '0.8rem', md: '1rem' },
|
||||
}}
|
||||
>
|
||||
Explorer les Articles
|
||||
</Button>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Hero;
|
||||
@@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
|
||||
const About = () => {
|
||||
return (
|
||||
<div>
|
||||
<h1>À propos</h1>
|
||||
<p>Bienvenue sur la page À propos.</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default About;
|
||||
@@ -0,0 +1,40 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import SEO from '../SEO';
|
||||
import api from '../../api';
|
||||
|
||||
const Contact = () => {
|
||||
// États pour le SEO
|
||||
const [metaTitle, setMetaTitle] = useState('Titre par défaut');
|
||||
const [metaDescription, setMetaDescription] = useState('Description par défaut.');
|
||||
|
||||
useEffect(() => {
|
||||
const fetchPageData = async () => {
|
||||
try {
|
||||
// Appel à l'API pour récupérer les données
|
||||
const response = await api.get('wp/v2/pages/116?_fields=acf,rank_math_title,rank_math_description');
|
||||
const { rank_math_title, rank_math_description } = response.data;
|
||||
|
||||
// Mise à jour des métadonnées SEO
|
||||
setMetaTitle(rank_math_title || 'Titre par défaut');
|
||||
setMetaDescription(rank_math_description || 'Description par défaut.');
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la récupération des données :', error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchPageData();
|
||||
}, []); // Exécution au premier rendu
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* SEO */}
|
||||
<SEO title={metaTitle} description={metaDescription} />
|
||||
|
||||
{/* Contenu de la page */}
|
||||
<h1>Contact</h1>
|
||||
<p>Bienvenue sur la page Contact.</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Contact;
|
||||
@@ -0,0 +1,99 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Hero from '../Hero';
|
||||
import SEO from '../SEO';
|
||||
import api from '../../api'; // Instance Axios configurée
|
||||
|
||||
const Home = () => {
|
||||
const [backgroundImage, setBackgroundImage] = useState(null);
|
||||
const [useGradient, setUseGradient] = useState(false);
|
||||
const [heroTitle, setHeroTitle] = useState('Titre par défaut');
|
||||
const [heroTitleColor, setHeroTitleColor] = useState('#ffffff');
|
||||
const [heroText, setHeroText] = useState('Texte par défaut');
|
||||
const [heroTextColor, setHeroTextColor] = useState('#ffffff');
|
||||
const [metaTitle, setMetaTitle] = useState('Titre par défaut');
|
||||
const [metaDescription, setMetaDescription] = useState('Description par défaut.');
|
||||
const [isLoading, setIsLoading] = useState(true); // État de chargement
|
||||
const [error, setError] = useState(null); // État des erreurs
|
||||
|
||||
useEffect(() => {
|
||||
const fetchPageData = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
const response = await api.get('wp/v2/pages/13?_fields=acf,rank_math_title,rank_math_description');
|
||||
const { acf, rank_math_title, rank_math_description } = response.data;
|
||||
|
||||
// Métadonnées SEO
|
||||
setMetaTitle(rank_math_title || 'Titre par défaut');
|
||||
setMetaDescription(rank_math_description || 'Description par défaut.');
|
||||
|
||||
// Données Hero
|
||||
setUseGradient(acf?.enable_gradient ?? false);
|
||||
setHeroTitle(acf?.hero_title || 'Titre par défaut');
|
||||
setHeroTitleColor(acf?.hero_title_color || '#ffffff');
|
||||
setHeroText(acf?.hero_text || 'Texte par défaut');
|
||||
setHeroTextColor(acf?.hero_text_color || '#ffffff');
|
||||
|
||||
// Image de fond
|
||||
if (acf?.img_hero) {
|
||||
const mediaResponse = await api.get(`wp/v2/media/${acf.img_hero}`);
|
||||
setBackgroundImage(mediaResponse.data.source_url);
|
||||
} else {
|
||||
setBackgroundImage(null);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la récupération des données :', error);
|
||||
setError('Impossible de charger les données. Veuillez réessayer.');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchPageData();
|
||||
}, []);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div style={{ textAlign: 'center', padding: '50px' }}>
|
||||
<p>Chargement des données...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div style={{ textAlign: 'center', padding: '50px' }}>
|
||||
<p style={{ color: 'red' }}>{error}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* SEO */}
|
||||
<SEO title={metaTitle} description={metaDescription} />
|
||||
|
||||
{/* Hero */}
|
||||
<Hero
|
||||
backgroundImage={backgroundImage}
|
||||
useGradient={useGradient}
|
||||
title={heroTitle}
|
||||
titleColor={heroTitleColor}
|
||||
text={heroText}
|
||||
textColor={heroTextColor}
|
||||
/>
|
||||
|
||||
{/* Contenu */}
|
||||
<div style={{ padding: '20px', textAlign: 'center' }}>
|
||||
<h2>À propos de notre site</h2>
|
||||
<p>
|
||||
Bienvenue sur notre site ! Découvrez nos articles, explorez des sujets passionnants,
|
||||
et profitez d'une expérience unique. Nous sommes heureux de vous accueillir.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
||||
@@ -0,0 +1,63 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Grid, Card, CardContent, CardMedia, Typography, Box } from '@mui/material';
|
||||
import api from '../../api'; // Adaptez le chemin vers l'instance Axios
|
||||
|
||||
|
||||
const Post = () => {
|
||||
const [posts, setPosts] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
api.get('wp/v2/posts?_embed')
|
||||
.then((response) => {
|
||||
setPosts(response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Erreur lors de la récupération des articles :', error);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
<Box sx={{ padding: 4 }}>
|
||||
<Typography variant="h3" sx={{ marginBottom: 4, textAlign: 'center' }}>
|
||||
Liste des Articles
|
||||
</Typography>
|
||||
<Grid container spacing={4}>
|
||||
{posts.map((post) => (
|
||||
<Grid item xs={12} sm={6} md={4} key={post.id}>
|
||||
<Card sx={{ maxWidth: 345,
|
||||
boxShadow: 3,
|
||||
transition: 'transform 0.3s ease, box-shadow 0.3s ease',
|
||||
'&:hover': {
|
||||
transform: 'scale(1.05)',
|
||||
boxShadow: 6,
|
||||
},
|
||||
}}>
|
||||
{/* Image de l'article */}
|
||||
{post._embedded?.['wp:featuredmedia']?.[0]?.source_url && (
|
||||
<CardMedia
|
||||
component="img"
|
||||
height="200"
|
||||
image={post._embedded['wp:featuredmedia'][0].source_url}
|
||||
alt={post.title.rendered}
|
||||
/>
|
||||
)}
|
||||
<CardContent>
|
||||
<Typography variant="h5" component="div" gutterBottom>
|
||||
{post.title.rendered}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="text.secondary"
|
||||
dangerouslySetInnerHTML={{ __html: post.excerpt.rendered }}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Post;
|
||||
@@ -0,0 +1,50 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
import api from "../../api";
|
||||
|
||||
const Search = () => {
|
||||
const [searchParams] = useSearchParams();
|
||||
const query = searchParams.get("query"); // Récupère la valeur de "query"
|
||||
const [results, setResults] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchSearchResults = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await api.get(`wp/v2/posts?search=${query}`);
|
||||
setResults(response.data);
|
||||
setLoading(false);
|
||||
} catch (error) {
|
||||
console.error("Erreur lors de la recherche :", error);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (query) {
|
||||
fetchSearchResults();
|
||||
}
|
||||
}, [query]);
|
||||
|
||||
return (
|
||||
<div style={{ padding: "20px" }}>
|
||||
<h1>Résultats pour : {query}</h1>
|
||||
{loading ? (
|
||||
<p>Chargement...</p>
|
||||
) : results.length > 0 ? (
|
||||
<ul>
|
||||
{results.map((post) => (
|
||||
<li key={post.id}>
|
||||
<h2>{post.title.rendered}</h2>
|
||||
<div dangerouslySetInnerHTML={{ __html: post.excerpt.rendered }} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p>Aucun résultat trouvé.</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Search;
|
||||
@@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet-async';
|
||||
|
||||
const SEO = ({ title, description }) => {
|
||||
return (
|
||||
<Helmet>
|
||||
{/* Title et Description */}
|
||||
<title>{title || 'Titre par défaut'}</title>
|
||||
<meta name="description" content={description || 'Description par défaut.'} />
|
||||
|
||||
{/* Preconnect */}
|
||||
<link rel="preconnect" href="https://octopusdesign.fr" />
|
||||
<link rel="preconnect" href="https://preprod.octopusdesign.fr" />
|
||||
|
||||
{/* DNS Prefetch */}
|
||||
<link rel="dns-prefetch" href="https://octopusdesign.fr" />
|
||||
<link rel="dns-prefetch" href="https://preprod.octopusdesign.fr" />
|
||||
</Helmet>
|
||||
);
|
||||
};
|
||||
|
||||
export default SEO;
|
||||
Reference in New Issue
Block a user