Maj Api and create contact page

This commit is contained in:
sebvtl728 2025-01-03 13:10:09 +01:00
parent 8fcd728887
commit 037cffaa0a
7 changed files with 994 additions and 81 deletions

View File

@ -2,6 +2,7 @@ import axios from 'axios';
const api = axios.create({
baseURL: 'https://preprod.octopusdesign.fr/api-octopus/server/wp-json',
withCredentials: true,
});
export default api;

View File

@ -124,19 +124,6 @@ const Header = () => {
{/* 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
@ -148,11 +135,11 @@ const Header = () => {
fontWeight: location.pathname.includes("/services")
? "bold"
: "normal",
textDecoration: location.pathname.includes("/services")
textDecoration: location.pathname.includes("/services")
? "underline"
: "none",
...commonButtonStyles,
}}
...commonButtonStyles,
}}
>
Services
</Button>
@ -191,6 +178,19 @@ const Header = () => {
</MenuItem>
</Menu>
<Button
component={Link}
to="/posts"
color="inherit"
sx={{
fontWeight: location.pathname === "/posts" ? "bold" : "normal",
textDecoration:
location.pathname === "/posts" ? "underline" : "none",
...commonButtonStyles,
}}
>
Articles
</Button>
<Button
component={Link}
to="/about"
@ -300,15 +300,6 @@ const Header = () => {
<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}
@ -336,6 +327,15 @@ const Header = () => {
<WorkIcon sx={{ marginRight: 1 }} />
<ListItemText primary="Formation Video" />
</ListItem>
<ListItem
button
component={Link}
to="/posts"
selected={location.pathname === "/posts"}
>
<ArticleIcon sx={{ marginRight: 1 }} />
<ListItemText primary="Articles" />
</ListItem>
<ListItem
button
component={Link}

View File

@ -1,40 +1,471 @@
import React, { useState, useEffect } from 'react';
import SEO from '../SEO';
import api from '../../api';
import React, { useState, useEffect } from "react";
import {
Box,
TextField,
Typography,
Button,
Grid,
Checkbox,
FormControlLabel,
Snackbar,
Alert,
CircularProgress,
Card,
CardContent,
Modal,
Fade,
Backdrop,
} from "@mui/material";
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.');
const [metaTitle, setMetaTitle] = useState("Contactez-nous");
const [metaDescription, setMetaDescription] = useState(
"Contactez notre équipe pour plus d'informations."
);
const [formData, setFormData] = useState({
name: "",
email: "",
subject: "",
message: "",
consent: false,
});
const [errors, setErrors] = useState({});
const [loading, setLoading] = useState(false);
const [successMessage, setSuccessMessage] = useState(false);
const [openLightbox, setOpenLightbox] = useState(false);
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;
useEffect(() => {
const fetchSEOData = async () => {
try {
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;
setMetaTitle(rank_math_title || "Contactez-nous");
setMetaDescription(rank_math_description || "Contactez notre équipe.");
} catch (error) {
console.error("Erreur lors de la récupération des données SEO :", error);
}
};
// 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);
fetchSEOData();
}, []);
const validate = () => {
const newErrors = {};
if (!formData.name) newErrors.name = "Le nom est requis.";
if (!formData.email || !/\S+@\S+\.\S+/.test(formData.email))
newErrors.email = "Une adresse e-mail valide est requise.";
if (!formData.subject) newErrors.subject = "Le sujet est requis.";
if (!formData.message) newErrors.message = "Le message est requis.";
if (!formData.consent)
newErrors.consent = "Vous devez accepter la politique de confidentialité.";
return newErrors;
};
const handleChange = (e) => {
const { name, value, type, checked } = e.target;
setFormData({
...formData,
[name]: type === "checkbox" ? checked : value,
});
};
const handleSubmit = async (e) => {
e.preventDefault();
const validationErrors = validate();
setErrors(validationErrors);
if (Object.keys(validationErrors).length === 0) {
setLoading(true);
try {
const response = await fetch(
"https://votre-site.com/wp-json/custom/v1/contact",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(formData),
}
);
if (response.ok) {
setSuccessMessage(true);
setFormData({
name: "",
email: "",
subject: "",
message: "",
consent: false,
});
} else {
const errorData = await response.json();
throw new Error(errorData.message || "Une erreur est survenue.");
}
} catch (error) {
alert(error.message);
} finally {
setLoading(false);
}
}
};
const handleOpenLightbox = () => {
setOpenLightbox(true);
};
const handleCloseLightbox = () => {
setOpenLightbox(false);
};
return (
<Box>
{/* SEO */}
<SEO title={metaTitle} description={metaDescription} />
{/* Section Attention */}
<Box
sx={{
backgroundImage: `url('https://via.placeholder.com/1920x800')`,
backgroundSize: "cover",
backgroundPosition: "center",
minHeight: "50vh",
display: "flex",
alignItems: "center",
justifyContent: "center",
textAlign: "center",
color: "white",
padding: "20px",
}}
>
<Box>
<Typography
variant="h2"
sx={{
fontWeight: "bold",
fontSize: { xs: "2rem", md: "3rem", lg: "4rem" },
mb: 2,
}}
>
Contactez-nous
</Typography>
<Typography
variant="h6"
sx={{
fontSize: { xs: "1rem", md: "1.5rem" },
maxWidth: "600px",
mx: "auto",
}}
>
Une question? Une demande particulière? Remplissez le formulaire
ci-dessous ou consultez nos coordonnées.
</Typography>
</Box>
</Box>
{/* Section Coordonnées et Horaires */}
<Box sx={{ padding: "40px 20px", backgroundColor: "#f9f9f9" }}>
<Grid container spacing={4}>
{/* Coordonnées */}
<Grid item xs={12} md={6}>
<Card
sx={{
boxShadow: 2,
borderRadius: 2,
padding: "20px",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
height: "80%",
}}
>
<Box>
<Typography
variant="h4"
sx={{
fontWeight: "bold",
color: "#0e467f",
mb: 2,
}}
>
Nos coordonnées
</Typography>
<Typography variant="body1">
<strong>Adresse :</strong> 123 Rue Exemple, Paris, France
</Typography>
<Typography variant="body1">
<strong>Téléphone :</strong> +33 1 23 45 67 89
</Typography>
<Typography variant="body1">
<strong>Email :</strong> contact@exemple.com
</Typography>
</Box>
<Box
onClick={handleOpenLightbox}
sx={{
position: "relative",
cursor: "pointer",
width: "120px",
height: "120px",
borderRadius: "8px",
overflow: "hidden",
boxShadow: 3,
"&:hover": {
transform: "scale(1.10)",
boxShadow: 4,
transition: "transform 0.3s, box-shadow 0.3s",
},
"&:hover::before": {
content: '"Cliquez pour agrandir"',
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
backgroundColor: "rgba(0, 0, 0, 0.7)",
color: "white",
padding: "5px 10px",
borderRadius: "4px",
fontSize: "12px",
},
}}
>
<img
src="https://picsum.photos/id/442/300.webp"
alt="Carte"
style={{ width: "100%", height: "100%", objectFit: "cover" }}
/>
</Box>
</Card>
</Grid>
{/* Horaires */}
<Grid item xs={12} md={6}>
<Card
sx={{
boxShadow: 2,
borderRadius: 2,
padding: "20px",
backgroundColor: "#fff",
height: "80%",
}}
>
<CardContent>
<Typography
variant="h4"
sx={{
fontWeight: "bold",
color: "#0e467f",
mb: 2,
textAlign: "center",
}}
>
Horaires d'ouverture
</Typography>
<Typography variant="body1">
<strong>Lundi - Vendredi :</strong> 9h00 - 18h00
</Typography>
<Typography variant="body1">
<strong>Samedi :</strong> 10h00 - 14h00
</Typography>
<Typography variant="body1">
<strong>Dimanche :</strong> Fermé
</Typography>
</CardContent>
</Card>
</Grid>
</Grid>
</Box>
{/* Lightbox */}
<Modal
open={openLightbox}
onClose={handleCloseLightbox}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{ timeout: 500 }}
>
<Fade in={openLightbox}>
<Box
sx={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: "90%",
maxWidth: "800px",
backgroundColor: "white",
boxShadow: 24,
borderRadius: 2,
overflow: "hidden",
}}
>
<iframe
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d2624.9998319324044!2d2.2922928156738935!3d48.85884407928788!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x47e66fdf78ccf6fd%3A0x10793db0b6d65f9b!2sEiffel+Tower!5e0!3m2!1sen!2sfr!4v1618237600000!5m2!1sen!2sfr"
width="100%"
height="400"
title="Carte interactive"
style={{ border: 0 }}
allowFullScreen=""
loading="lazy"
/>
</Box>
</Fade>
</Modal>
{/* Section Action */}
<Box
sx={{
backgroundColor: "#0e467f",
color: "white",
textAlign: "center",
padding: "40px 20px",
}}
>
<Typography
variant="h4"
sx={{
fontWeight: "bold",
mb: 2,
fontSize: { xs: "1.5rem", md: "2rem" },
}}
>
Besoin d'une assistance immédiate?
</Typography>
<Button
href="/aide"
variant="contained"
sx={{
textTransform: "none",
fontWeight: "bold",
backgroundColor: "#00bcd4",
"&:hover": { backgroundColor: "#0288d1" },
}}
>
Obtenez de l'aide maintenant
</Button>
</Box>
{/* Formulaire de contact */}
<Box
sx={{
maxWidth: "800px",
margin: "auto",
padding: "40px",
boxShadow: 3,
borderRadius: 2,
backgroundColor: "#fff",
mt: 4,
}}
>
<Typography
variant="h4"
sx={{ mb: 4, textAlign: "center", fontWeight: "bold", color: "#0e467f" }}
>
Formulaire de contact
</Typography>
<form onSubmit={handleSubmit} noValidate>
<TextField
label="Nom"
name="name"
value={formData.name}
onChange={handleChange}
error={Boolean(errors.name)}
helperText={errors.name}
fullWidth
margin="normal"
variant="outlined"
/>
<TextField
label="E-mail"
name="email"
value={formData.email}
onChange={handleChange}
error={Boolean(errors.email)}
helperText={errors.email}
fullWidth
margin="normal"
variant="outlined"
/>
<TextField
label="Sujet"
name="subject"
value={formData.subject}
onChange={handleChange}
error={Boolean(errors.subject)}
helperText={errors.subject}
fullWidth
margin="normal"
variant="outlined"
/>
<TextField
label="Message"
name="message"
value={formData.message}
onChange={handleChange}
error={Boolean(errors.message)}
helperText={errors.message}
fullWidth
margin="normal"
variant="outlined"
multiline
rows={4}
/>
<FormControlLabel
control={
<Checkbox
name="consent"
checked={formData.consent}
onChange={handleChange}
/>
}
};
label="J'accepte la politique de confidentialité."
sx={{ mt: 2, color: errors.consent ? "red" : "inherit" }}
/>
{errors.consent && (
<Typography variant="caption" color="error">
{errors.consent}
</Typography>
)}
<Box sx={{ textAlign: "center", mt: 3 }}>
<Button
type="submit"
variant="contained"
color="primary"
size="large"
disabled={loading}
sx={{
textTransform: "none",
fontWeight: "bold",
px: 5,
}}
>
{loading ? (
<CircularProgress size={24} sx={{ color: "#fff" }} />
) : (
"Envoyer"
)}
</Button>
</Box>
</form>
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>
);
<Snackbar
open={successMessage}
autoHideDuration={6000}
onClose={() => setSuccessMessage(false)}
>
<Alert onClose={() => setSuccessMessage(false)} severity="success">
Message envoyé avec succès!
</Alert>
</Snackbar>
</Box>
</Box>
);
};
export default Contact;

View File

@ -15,7 +15,6 @@ import { Navigation, Pagination, Autoplay } from "swiper/modules";
import "swiper/css";
import "swiper/css/navigation";
import "swiper/css/pagination";
import { useIsTouchDevice } from "./hooks/useIsTouchDevice"; // Import du hook
const ServicePageTemplate = ({
title,
@ -29,7 +28,6 @@ const ServicePageTemplate = ({
}) => {
const [openModal, setOpenModal] = useState(false);
const [modalContent, setModalContent] = useState({});
const isTouchDevice = useIsTouchDevice(); // Détection des écrans tactiles
const handleCardClick = (feature) => {
setModalContent({
@ -68,18 +66,25 @@ const ServicePageTemplate = ({
}}
>
<Box sx={{ maxWidth: "800px" }}>
<Typography
variant="h1"
sx={{
fontWeight: "bold",
mb: 2,
fontSize: { xs: "2rem", md: "3rem", lg: "4rem" },
<Typography
variant="h1"
sx={{
fontWeight: "bold",
mb: 2,
fontSize: { xs: '2rem', md: '3rem', lg: '4rem' }, // Responsive
}}
>
{title}
</Typography>
<Typography variant="body1" sx={{ mb: 4 }}>
<Typography
variant="body1"
sx={{
mb: 4
}}
>
{subtitle}
</Typography>
<Button
href={ctaLink}
@ -100,16 +105,16 @@ const ServicePageTemplate = ({
{/* Section Interest */}
<Box sx={{ padding: "40px 20px", textAlign: "center" }}>
<Typography
variant="h2"
sx={{
fontWeight: "bold",
<Typography variant="h2"
sx={{
fontWeight: "bold",
mb: 2,
fontSize: { xs: "2rem", md: "3rem", lg: "3rem" },
fontSize: { xs: '2rem', md: '3rem', lg: '3rem' }, // Responsive
}}
>
Pourquoi choisir ce service ?
Pourquoi choisir notre formation ?
</Typography>
<Typography
variant="body1"
sx={{ maxWidth: "800px", margin: "0 auto", mb: 4 }}
@ -122,15 +127,17 @@ const ServicePageTemplate = ({
<Box sx={{ backgroundColor: "#ffffff", padding: "40px 20px" }}>
<Typography
variant="h2"
sx={{
fontWeight: "bold",
textAlign: "center",
mb: 4,
fontSize: { xs: "2rem", md: "3rem", lg: "3rem" },
}}
sx={{
fontWeight: "bold",
textAlign: "center",
mb: 4,
fontSize: { xs: '2rem', md: '3rem', lg: '3rem' }, // Responsive
}}
>
Ce que nous offrons
</Typography>
<Grid container spacing={4} justifyContent="center">
{features.map((feature, index) => (
<Grid item xs={12} md={4} key={index}>
@ -144,11 +151,17 @@ const ServicePageTemplate = ({
cursor: "pointer",
transition: "transform 0.3s ease, box-shadow 0.3s ease",
"&:hover": {
transform: isTouchDevice ? "none" : "scale(1.05)",
boxShadow: isTouchDevice ? 2 : 5,
transform: {
xs: "none", // Pas d'hover sur mobile
md: "scale(1.05)", // Hover sur desktop uniquement
},
boxShadow: {
xs: 2, // Pas d'effet de hover sur mobile
md: 5, // Augmentation de l'ombre sur desktop
},
},
"&:active": {
transform: "scale(0.98)",
transform: "scale(0.98)", // Effet de clic pour le tactile
boxShadow: 3,
},
}}
@ -238,8 +251,115 @@ const ServicePageTemplate = ({
</Box>
</Fade>
</Modal>
{/* Carousel Section */}
{carouselItems && carouselItems.length > 0 && (
<Box
sx={{
padding: "40px 20px",
textAlign: "center",
backgroundColor: "#f9f9f9",
}}
>
<Typography variant="h2"
sx={{
marginBottom: 4,
fontSize: { xs: '2rem', md: '3rem', lg: '3rem' }, // Responsive
}}
>
Découvrez nos réalisations
</Typography>
<Swiper
modules={[Navigation, Pagination, Autoplay]}
spaceBetween={20}
slidesPerView={1}
navigation
pagination={{ clickable: true }}
autoplay={{ delay: 3000 }}
loop
>
{carouselItems.map((item, index) => (
<SwiperSlide key={index}>
<Card
sx={{
display: "flex",
alignItems: "center",
padding: "20px",
boxShadow: 3,
}}
>
<img
src={item.image}
alt={item.title}
style={{
width: "40%",
height: "auto",
objectFit: "cover",
borderRadius: "8px",
marginRight: "20px",
}}
/>
<CardContent>
<Typography variant="h3"
sx={{
fontWeight: "bold",
fontSize: { xs: '1.2rem', md: '2rem', lg: '2.5rem' }, // Responsive
}}
>
{item.title}
</Typography>
<Typography variant="body2">{item.description}</Typography>
</CardContent>
</Card>
</SwiperSlide>
))}
</Swiper>
</Box>
)}
{/* Section Action */}
<Box
sx={{
backgroundColor: "#0e467f",
color: "white",
textAlign: "center",
padding: "40px 20px",
}}
>
<Typography variant="h2"
sx={{
fontWeight: "bold",
mb: 2,
fontSize: { xs: '2rem', md: '3rem', lg: '3rem' }, // Responsive
}}
>
Prêt à démarrer ?
</Typography>
<Typography variant="body1" sx={{ mb: 4 }}>
Rejoignez-nous dès aujourdhui et profitez de nos services pour
booster votre activité.
</Typography>
<Button
href={ctaLink}
variant="contained"
color="secondary"
size="large"
sx={{
textTransform: "none",
fontWeight: "bold",
backgroundColor: "#00bcd4",
"&:hover": { backgroundColor: "#0288d1" },
}}
>
{ctaText}
</Button>
</Box>
</Box>
);
};
export default ServicePageTemplate;
export default ServicePageTemplate;

16
server/.htaccess Normal file
View File

@ -0,0 +1,16 @@
# BEGIN WordPress
# Les directives (lignes) entre « BEGIN WordPress » et « END WordPress » sont générées
# dynamiquement, et doivent être modifiées uniquement via les filtres WordPress.
# Toute modification des directives situées entre ces marqueurs sera surchargée.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /api-octopus/server/
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /api-octopus/server/index.php [L]
</IfModule>
# END WordPress

View File

@ -0,0 +1,5 @@
# BEGIN WebP Express
# Plugin is deactivated
# END WebP Express

View File

@ -0,0 +1,340 @@
<?php
/**
* Theme functions and definitions
*
* @package HelloElementor
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
define( 'HELLO_ELEMENTOR_VERSION', '3.2.1' );
if ( ! isset( $content_width ) ) {
$content_width = 800; // Pixels.
}
if ( ! function_exists( 'hello_elementor_setup' ) ) {
/**
* Set up theme support.
*
* @return void
*/
function hello_elementor_setup() {
if ( is_admin() ) {
hello_maybe_update_theme_version_in_db();
}
if ( apply_filters( 'hello_elementor_register_menus', true ) ) {
register_nav_menus( [ 'menu-1' => esc_html__( 'Header', 'hello-elementor' ) ] );
register_nav_menus( [ 'menu-2' => esc_html__( 'Footer', 'hello-elementor' ) ] );
}
if ( apply_filters( 'hello_elementor_post_type_support', true ) ) {
add_post_type_support( 'page', 'excerpt' );
}
if ( apply_filters( 'hello_elementor_add_theme_support', true ) ) {
add_theme_support( 'post-thumbnails' );
add_theme_support( 'automatic-feed-links' );
add_theme_support( 'title-tag' );
add_theme_support(
'html5',
[
'search-form',
'comment-form',
'comment-list',
'gallery',
'caption',
'script',
'style',
]
);
add_theme_support(
'custom-logo',
[
'height' => 100,
'width' => 350,
'flex-height' => true,
'flex-width' => true,
]
);
add_theme_support( 'align-wide' );
add_theme_support( 'responsive-embeds' );
/*
* Editor Styles
*/
add_theme_support( 'editor-styles' );
add_editor_style( 'editor-styles.css' );
/*
* WooCommerce.
*/
if ( apply_filters( 'hello_elementor_add_woocommerce_support', true ) ) {
// WooCommerce in general.
add_theme_support( 'woocommerce' );
// Enabling WooCommerce product gallery features (are off by default since WC 3.0.0).
// zoom.
add_theme_support( 'wc-product-gallery-zoom' );
// lightbox.
add_theme_support( 'wc-product-gallery-lightbox' );
// swipe.
add_theme_support( 'wc-product-gallery-slider' );
}
}
}
}
add_action( 'after_setup_theme', 'hello_elementor_setup' );
function hello_maybe_update_theme_version_in_db() {
$theme_version_option_name = 'hello_theme_version';
// The theme version saved in the database.
$hello_theme_db_version = get_option( $theme_version_option_name );
// If the 'hello_theme_version' option does not exist in the DB, or the version needs to be updated, do the update.
if ( ! $hello_theme_db_version || version_compare( $hello_theme_db_version, HELLO_ELEMENTOR_VERSION, '<' ) ) {
update_option( $theme_version_option_name, HELLO_ELEMENTOR_VERSION );
}
}
if ( ! function_exists( 'hello_elementor_display_header_footer' ) ) {
/**
* Check whether to display header footer.
*
* @return bool
*/
function hello_elementor_display_header_footer() {
$hello_elementor_header_footer = true;
return apply_filters( 'hello_elementor_header_footer', $hello_elementor_header_footer );
}
}
if ( ! function_exists( 'hello_elementor_scripts_styles' ) ) {
/**
* Theme Scripts & Styles.
*
* @return void
*/
function hello_elementor_scripts_styles() {
$min_suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
if ( apply_filters( 'hello_elementor_enqueue_style', true ) ) {
wp_enqueue_style(
'hello-elementor',
get_template_directory_uri() . '/style' . $min_suffix . '.css',
[],
HELLO_ELEMENTOR_VERSION
);
}
if ( apply_filters( 'hello_elementor_enqueue_theme_style', true ) ) {
wp_enqueue_style(
'hello-elementor-theme-style',
get_template_directory_uri() . '/theme' . $min_suffix . '.css',
[],
HELLO_ELEMENTOR_VERSION
);
}
if ( hello_elementor_display_header_footer() ) {
wp_enqueue_style(
'hello-elementor-header-footer',
get_template_directory_uri() . '/header-footer' . $min_suffix . '.css',
[],
HELLO_ELEMENTOR_VERSION
);
}
}
}
add_action( 'wp_enqueue_scripts', 'hello_elementor_scripts_styles' );
if ( ! function_exists( 'hello_elementor_register_elementor_locations' ) ) {
/**
* Register Elementor Locations.
*
* @param ElementorPro\Modules\ThemeBuilder\Classes\Locations_Manager $elementor_theme_manager theme manager.
*
* @return void
*/
function hello_elementor_register_elementor_locations( $elementor_theme_manager ) {
if ( apply_filters( 'hello_elementor_register_elementor_locations', true ) ) {
$elementor_theme_manager->register_all_core_location();
}
}
}
add_action( 'elementor/theme/register_locations', 'hello_elementor_register_elementor_locations' );
if ( ! function_exists( 'hello_elementor_content_width' ) ) {
/**
* Set default content width.
*
* @return void
*/
function hello_elementor_content_width() {
$GLOBALS['content_width'] = apply_filters( 'hello_elementor_content_width', 800 );
}
}
add_action( 'after_setup_theme', 'hello_elementor_content_width', 0 );
if ( ! function_exists( 'hello_elementor_add_description_meta_tag' ) ) {
/**
* Add description meta tag with excerpt text.
*
* @return void
*/
function hello_elementor_add_description_meta_tag() {
if ( ! apply_filters( 'hello_elementor_description_meta_tag', true ) ) {
return;
}
if ( ! is_singular() ) {
return;
}
$post = get_queried_object();
if ( empty( $post->post_excerpt ) ) {
return;
}
echo '<meta name="description" content="' . esc_attr( wp_strip_all_tags( $post->post_excerpt ) ) . '">' . "\n";
}
}
add_action( 'wp_head', 'hello_elementor_add_description_meta_tag' );
// Admin notice
if ( is_admin() ) {
require get_template_directory() . '/includes/admin-functions.php';
}
// Settings page
require get_template_directory() . '/includes/settings-functions.php';
// Header & footer styling option, inside Elementor
require get_template_directory() . '/includes/elementor-functions.php';
if ( ! function_exists( 'hello_elementor_customizer' ) ) {
// Customizer controls
function hello_elementor_customizer() {
if ( ! is_customize_preview() ) {
return;
}
if ( ! hello_elementor_display_header_footer() ) {
return;
}
require get_template_directory() . '/includes/customizer-functions.php';
}
}
add_action( 'init', 'hello_elementor_customizer' );
if ( ! function_exists( 'hello_elementor_check_hide_title' ) ) {
/**
* Check whether to display the page title.
*
* @param bool $val default value.
*
* @return bool
*/
function hello_elementor_check_hide_title( $val ) {
if ( defined( 'ELEMENTOR_VERSION' ) ) {
$current_doc = Elementor\Plugin::instance()->documents->get( get_the_ID() );
if ( $current_doc && 'yes' === $current_doc->get_settings( 'hide_title' ) ) {
$val = false;
}
}
return $val;
}
}
add_filter( 'hello_elementor_page_title', 'hello_elementor_check_hide_title' );
/**
* BC:
* In v2.7.0 the theme removed the `hello_elementor_body_open()` from `header.php` replacing it with `wp_body_open()`.
* The following code prevents fatal errors in child themes that still use this function.
*/
if ( ! function_exists( 'hello_elementor_body_open' ) ) {
function hello_elementor_body_open() {
wp_body_open();
}
}
// Ajouter les champs ACF à l'API REST pour les pages
add_action('rest_api_init', function () {
register_rest_field(
'page',
'acf',
[
'get_callback' => function ($object) {
return get_fields($object['id']);
},
'schema' => null,
]
);
});
// API RANK SEO
add_filter('rest_prepare_page', function ($response, $post) {
$seo_data = [
'rank_math_title' => get_post_meta($post->ID, 'rank_math_title', true),
'rank_math_description' => get_post_meta($post->ID, 'rank_math_description', true),
];
$response->data = array_merge($response->data, $seo_data);
return $response;
}, 10, 2);
add_action('rest_api_init', function () {
register_rest_route('custom/v1', '/contact', array(
'methods' => 'POST',
'callback' => 'handle_contact_form',
'permission_callback' => '__return_true', // Permet l'accès public
));
});
// Formulaire
function register_contact_endpoint() {
register_rest_route('custom/v1', '/contact', [
'methods' => 'POST',
'callback' => 'handle_contact_form',
'permission_callback' => '__return_true',
]);
}
add_action('rest_api_init', 'register_contact_endpoint');
function handle_contact_form($request) {
$params = $request->get_json_params();
$name = sanitize_text_field($params['name'] ?? '');
$email = sanitize_email($params['email'] ?? '');
$subject = sanitize_text_field($params['subject'] ?? '');
$message = sanitize_textarea_field($params['message'] ?? '');
if (empty($name) || empty($email) || empty($subject) || empty($message)) {
return new WP_Error(
'incomplete_fields',
'Tous les champs doivent être remplis.',
['status' => 400]
);
}
// Logique pour envoyer l'e-mail ou enregistrer les données
$to = get_option('admin_email'); // Adresse e-mail de l'administrateur WordPress
$headers = ['Content-Type: text/html; charset=UTF-8', 'From: ' . $name . ' <' . $email . '>'];
$mail_sent = wp_mail($to, $subject, nl2br($message), $headers);
if (!$mail_sent) {
return new WP_Error('email_not_sent', 'Le message na pas pu être envoyé.', ['status' => 500]);
}
return [
'success' => true,
'message' => 'Message envoyé avec succès.',
];
}