feat: préparation Payload CMS — couche d'abstraction contenu

Sépare données et affichage pour basculer vers Payload CMS sans réécrire les composants.

Nouveaux fichiers :
- lib/site-config.ts : source unique de vérité pour toutes les données du site (as const)
- lib/content.ts : couche async entre données et composants (static aujourd'hui, Payload demain)
- types/content.ts : types TypeScript partagés (Service, Realisation, Partner, BlogPost, etc.)
- payload/ : schémas CollectionConfig et GlobalConfig commentés prêts à activer

Données enrichies dans siteConfig :
- partners : ajout du champ desc pour chaque partenaire
- realisations : 6 entrées complètes avec categorie et color
- blogPosts : 6 articles avec slug, titre, extrait, cat, date, readTime

Refactorisations (composants → content layer) :
- Navbar, Footer : importent siteConfig directement (client component)
- app/page.tsx : async, Promise.all sur getServices/getTestimonials/getFAQ/getValues/getPartners/getRealisations
- app/services/page.tsx : getServices() + getSiteConfig()
- app/contact/page.tsx : getSiteConfig() pour phone, email, address, zones
- app/realisations/page.tsx : getRealisations() + getSiteConfig()
- app/partenaires/page.tsx : getPartners()
- app/blog/page.tsx : getBlogPosts()
- app/blog/[slug]/page.tsx : getBlogPost() + getBlogPosts() pour generateStaticParams
- LocalSEOPage.tsx : siteConfig pour services list, phone, address
- 5 pages service (construction-maison, renovation, assainissement, creation-acces, demolition) : getSiteConfig() pour phone
- Pages légales et SEO locales : siteConfig importé pour données dynamiques

Corrections URL :
- Toutes les URLs canoniques obc-maconnerie.fr → obc-terrassement.fr (30+ fichiers)
- layout.tsx : BASE_URL depuis siteConfig.url
- robots.ts, sitemap.ts : BASE_URL depuis siteConfig.url
- api/contact/route.ts : email fallback → obc-terrassement.fr

https://claude.ai/code/session_01Uec4iHjcPwB1pU41idWEdF
This commit is contained in:
Claude
2026-02-27 13:05:19 +00:00
parent 3adcec00b7
commit 15c60a274c
40 changed files with 1534 additions and 860 deletions

View File

@@ -31,8 +31,8 @@ export async function POST(request: Request) {
const { Resend } = await import("resend"); const { Resend } = await import("resend");
const resend = new Resend(process.env.RESEND_API_KEY); const resend = new Resend(process.env.RESEND_API_KEY);
const fromEmail = process.env.RESEND_FROM_EMAIL || "OBC Maçonnerie <contact@obc-maconnerie.fr>"; const fromEmail = process.env.RESEND_FROM_EMAIL || "OBC Maçonnerie <contact@obc-terrassement.fr>";
const adminEmail = process.env.ADMIN_EMAIL || "contact@obc-maconnerie.fr"; const adminEmail = process.env.ADMIN_EMAIL || "contact@obc-terrassement.fr";
await resend.emails.send({ await resend.emails.send({
from: fromEmail, from: fromEmail,

View File

@@ -4,8 +4,12 @@ import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer"; import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal"; import ScrollReveal from "@/components/animations/ScrollReveal";
import ContactForm from "@/components/marketing/ContactForm"; import ContactForm from "@/components/marketing/ContactForm";
import { getSiteConfig } from "@/lib/content";
export async function generateMetadata(): Promise<Metadata> {
const config = await getSiteConfig();
return {
export const metadata: Metadata = {
title: "Assainissement Maison Nord 59 | OBC Maçonnerie", title: "Assainissement Maison Nord 59 | OBC Maçonnerie",
description: description:
"Création et mise aux normes de systèmes d'assainissement dans le Nord (59). OBC Maçonnerie intervient à Orchies, Douai, Valenciennes et alentours. Devis gratuit.", "Création et mise aux normes de systèmes d'assainissement dans le Nord (59). OBC Maçonnerie intervient à Orchies, Douai, Valenciennes et alentours. Devis gratuit.",
@@ -17,8 +21,9 @@ export const metadata: Metadata = {
"assainissement Orchies", "assainissement Orchies",
"assainissement Douai", "assainissement Douai",
], ],
alternates: { canonical: "https://obc-maconnerie.fr/assainissement" }, alternates: { canonical: `${config.url}/assainissement` },
}; };
}
const prestations = [ const prestations = [
{ icon: "🔍", title: "Diagnostic", desc: "Analyse de votre installation existante et vérification de sa conformité aux normes en vigueur." }, { icon: "🔍", title: "Diagnostic", desc: "Analyse de votre installation existante et vérification de sa conformité aux normes en vigueur." },
@@ -29,7 +34,9 @@ const prestations = [
{ icon: "💧", title: "Raccordement réseau", desc: "Connexion au réseau d'assainissement collectif lorsque celui-ci est disponible." }, { icon: "💧", title: "Raccordement réseau", desc: "Connexion au réseau d'assainissement collectif lorsque celui-ci est disponible." },
]; ];
export default function AssainissementPage() { export default async function AssainissementPage() {
const config = await getSiteConfig();
const { phone, phoneRaw } = config;
return ( return (
<main id="main-content" className="min-h-screen"> <main id="main-content" className="min-h-screen">
<Navbar /> <Navbar />
@@ -55,9 +62,7 @@ export default function AssainissementPage() {
<Link href="/contact" className="inline-flex items-center justify-center gap-2 bg-orange hover:bg-orange-hover text-white font-bold px-7 py-3.5 rounded-xl transition-colors pulse-glow"> <Link href="/contact" className="inline-flex items-center justify-center gap-2 bg-orange hover:bg-orange-hover text-white font-bold px-7 py-3.5 rounded-xl transition-colors pulse-glow">
Demander un devis gratuit Demander un devis gratuit
</Link> </Link>
<a href="tel:0674453089" className="inline-flex items-center justify-center gap-2 bg-white/10 hover:bg-white/20 text-white font-semibold px-7 py-3.5 rounded-xl transition-colors border border-white/20"> <a href={`tel:${phoneRaw}`} className="inline-flex items-center justify-center gap-2 bg-white/10 hover:bg-white/20 text-white font-semibold px-7 py-3.5 rounded-xl transition-colors border border-white/20">{phone}</a>
06 74 45 30 89
</a>
</div> </div>
</ScrollReveal> </ScrollReveal>
</div> </div>

View File

@@ -4,137 +4,85 @@ import { notFound } from "next/navigation";
import Navbar from "@/components/marketing/Navbar"; import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer"; import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal"; import ScrollReveal from "@/components/animations/ScrollReveal";
import { getBlogPost, getBlogPosts, getSiteConfig } from "@/lib/content";
type Props = { params: Promise<{ slug: string }> }; type Props = { params: Promise<{ slug: string }> };
const articles: Record< // Corps des articles — FUTURE: champ rich text Payload CMS
string, const articleContenu: Record<string, string[]> = {
{ "combien-coute-construction-maison-nord": [
titre: string; "La construction d'une maison individuelle dans le Nord représente un investissement significatif. En 2025, le coût moyen se situe entre 1 200 € et 1 800 € par m² hors terrain et hors raccordements, selon les matériaux choisis et la complexité du projet.",
description: string; "**Le prix du terrain** est souvent la première variable. Dans le secteur d'Orchies et de Mouchin, comptez entre 50 000 € et 120 000 € pour une parcelle constructible de 400 à 600 m².",
cat: string; "**Le gros œuvre** (fondations, murs, dalle, toiture) représente environ 40 à 50% du budget total. C'est là qu'intervient OBC Maçonnerie avec son savoir-faire et son réseau de partenaires pour optimiser les coûts sans sacrifier la qualité.",
date: string; "**Les finitions et corps de métier** (électricité, plomberie, chauffage, isolation, menuiserie, carrelage, peinture) représentent les 50 à 60% restants.",
readTime: string; "Pour un projet de 100 m² habitable à Orchies ou Douai, un budget total entre 180 000 € et 280 000 € (hors terrain) est réaliste selon le niveau de finition souhaité.",
contenu: string[]; "Le meilleur conseil : contactez Benoît Colin pour une évaluation gratuite de votre projet. Il vous donnera une estimation précise adaptée à votre terrain et vos envies.",
} ],
> = { "etapes-renovation-maison-ancienne": [
"combien-coute-construction-maison-nord": { "Rénover une maison ancienne dans le Nord demande une méthodologie rigoureuse. Voici les grandes étapes pour mener votre projet à bien.",
titre: "Combien coûte la construction d'une maison dans le Nord en 2025 ?", "**1. Le diagnostic** : Avant tout, il faut évaluer l'état du bâtiment. Charpente, toiture, murs porteurs, fondations, réseaux électriques et plomberie — tout doit être passé en revue. Un maçon expérimenté comme Benoît Colin peut repérer les problèmes invisibles à l'œil nu.",
description: "**2. La démolition et le curage** : On enlève ce qui est vétuste ou inadapté — cloisons obsolètes, chapes abîmées, enduits défaillants — pour repartir sur de bonnes bases.",
"Budget, matériaux, terrain, main-d'œuvre — tout ce qu'il faut savoir pour estimer le coût de votre construction neuve dans le Nord.", "**3. Le gros œuvre** : Reprises de fondations si nécessaire, traitement des murs humides, création d'ouvertures, modification de la structure. C'est le cœur du métier d'OBC Maçonnerie.",
cat: "Construction", "**4. Les corps de métier** : Électricité, plomberie, chauffage, isolation. Grâce à son réseau de partenaires, Benoît coordonne chaque intervention dans le bon ordre.",
date: "15 février 2025", "**5. Les finitions** : Menuiseries, carrelage, peinture, revêtements de sol. La touche finale qui donne tout son caractère à votre maison rénovée.",
readTime: "6 min", "Chaque rénovation est unique. Contactez OBC Maçonnerie pour une évaluation gratuite de votre projet.",
contenu: [ ],
"La construction d'une maison individuelle dans le Nord représente un investissement significatif. En 2025, le coût moyen se situe entre 1 200 € et 1 800 € par m² hors terrain et hors raccordements, selon les matériaux choisis et la complexité du projet.", "assainissement-non-collectif-obligations": [
"**Le prix du terrain** est souvent la première variable. Dans le secteur d'Orchies et de Mouchin, comptez entre 50 000 € et 120 000 € pour une parcelle constructible de 400 à 600 m².", "En France, environ 5 millions de logements sont équipés d'un assainissement non collectif (ANC). Si votre maison n'est pas raccordée au réseau public, vous êtes soumis à des obligations précises.",
"**Le gros œuvre** (fondations, murs, dalle, toiture) représente environ 40 à 50% du budget total. C'est là qu'intervient OBC Maçonnerie avec son savoir-faire et son réseau de partenaires pour optimiser les coûts sans sacrifier la qualité.", "**Le contrôle SPANC** : Le Service Public d'Assainissement Non Collectif peut contrôler votre installation. En cas de non-conformité, vous avez en principe 4 ans pour mettre aux normes, ou moins si vous vendez le bien.",
"**Les finitions et corps de métier** (électricité, plomberie, chauffage, isolation, menuiserie, carrelage, peinture) représentent les 50 à 60% restants.", "**La vente immobilière** : Depuis 2011, un diagnostic d'assainissement est obligatoire lors de toute vente. S'il révèle une non-conformité, l'acheteur doit réaliser les travaux dans l'année suivant l'acte de vente.",
"Pour un projet de 100 m² habitable à Orchies ou Douai, un budget total entre 180 000 € et 280 000 € (hors terrain) est réaliste selon le niveau de finition souhaité.", "**Les principales normes** : Votre installation doit traiter correctement les eaux usées avant rejet dans le sol. Les normes imposent une fosse toutes eaux (ou une micro-station) et un dispositif d'épandage adapté à la surface disponible.",
"Le meilleur conseil : contactez Benoît Colin pour une évaluation gratuite de votre projet. Il vous donnera une estimation précise adaptée à votre terrain et vos envies.", "**OBC Maçonnerie vous accompagne** dans la mise aux normes ou la création de votre système d'assainissement non collectif. Nous intervenons sur Orchies, Douai, Valenciennes et toute la zone.",
], ],
}, "ossature-bois-avantages": [
"etapes-renovation-maison-ancienne": { "La construction en ossature bois connaît un vrai succès dans le Nord. Et pour cause : ce mode constructif présente de nombreux avantages techniques et économiques.",
titre: "Les étapes clés d'une rénovation de maison ancienne", "**Performance thermique** : Le bois est un excellent isolant naturel. Une construction ossature bois bien conçue atteint facilement les exigences RE2020.",
description: "**Rapidité de chantier** : Les éléments préfabriqués permettent de monter les murs en quelques jours. Le clos-couvert est obtenu très rapidement.",
"Vous avez acheté une maison ancienne dans le Nord et vous voulez la rénover ? Voici les étapes indispensables pour réussir votre projet.", "**Légèreté** : L'ossature bois pèse 5 à 8 fois moins qu'une construction maçonnée, ce qui allège les fondations — un avantage sur les terrains argileux fréquents dans le Nord.",
cat: "Rénovation", "**Polyvalence architecturale** : L'ossature bois permet des formes architecturales variées, des larges baies vitrées et une grande liberté de conception.",
date: "8 janvier 2025", "**La combinaison idéale** : OBC Maçonnerie maîtrise la construction ossature bois et la maçonnerie traditionnelle. Benoît vous conseille sur la solution la plus adaptée à votre terrain et vos envies.",
readTime: "5 min", ],
contenu: [ "travaux-renovation-sans-permis-construction": [
"Rénover une maison ancienne dans le Nord demande une méthodologie rigoureuse. Voici les grandes étapes pour mener votre projet à bien.", "Avant de démarrer des travaux de rénovation, il est important de savoir si vous avez besoin d'une autorisation administrative.",
"**1. Le diagnostic** : Avant tout, il faut évaluer l'état du bâtiment. Charpente, toiture, murs porteurs, fondations, réseaux électriques et plomberie — tout doit être passé en revue. Un maçon expérimenté comme Benoît Colin peut repérer les problèmes invisibles à l'œil nu.", "**Aucune démarche requise** : Les travaux purement intérieurs (peinture, revêtements, redistribution de cloisons non porteuses, remplacement de fenêtres à l'identique) ne nécessitent généralement aucune démarche.",
"**2. La démolition et le curage** : On enlève ce qui est vétuste ou inadapté — cloisons obsolètes, chapes abîmées, enduits défaillants — pour repartir sur de bonnes bases.", "**Déclaration préalable** : Pour les extensions jusqu'à 40 m² (en zone urbaine PLU), les changements de façade, les travaux modifiant l'aspect extérieur.",
"**3. Le gros œuvre** : Reprises de fondations si nécessaire, traitement des murs humides, création d'ouvertures, modification de la structure. C'est le cœur du métier d'OBC Maçonnerie.", "**Permis de construire** : Obligatoire pour les extensions de plus de 40 m², la création d'une surface de plancher supérieure à 20 m² en dehors des zones PLU, ou les changements de destination.",
"**4. Les corps de métier** : Électricité, plomberie, chauffage, isolation. Grâce à son réseau de partenaires, Benoît coordonne chaque intervention dans le bon ordre.", "**Cas des zones protégées** : Si votre maison est en zone ABF (Architecte des Bâtiments de France), les règles sont plus strictes. Renseignez-vous en mairie.",
"**5. Les finitions** : Menuiseries, carrelage, peinture, revêtements de sol. La touche finale qui donne tout son caractère à votre maison rénovée.", "En cas de doute, OBC Maçonnerie vous accompagne dans vos démarches administratives. Benoît connaît bien les règles locales dans le secteur d'Orchies, Douai et Valenciennes.",
"Chaque rénovation est unique. Contactez OBC Maçonnerie pour une évaluation gratuite de votre projet.", ],
], "fondations-maison-quels-types": [
}, "Les fondations sont la base de toute construction. Mal dimensionnées ou inadaptées au sol, elles peuvent entraîner des désordres graves. Voici les principaux types.",
"assainissement-non-collectif-obligations": { "**Les semelles filantes** : Le type le plus courant pour les maisons individuelles. Elles répartissent les charges des murs porteurs sur une bande de terrain. Adaptées aux sols stables et homogènes.",
titre: "Assainissement non collectif : vos obligations légales", "**Le radier** : Une dalle béton armé qui couvre toute la surface de la maison. Recommandé sur les terrains argileux, instables ou avec présence d'eau. Fréquent dans certains secteurs du Nord.",
description: "**Les pieux** : Utilisés quand le sol de surface est insuffisant pour porter la maison. Des pieux sont enfoncés jusqu'à une couche de sol plus résistante.",
"Contrôle SPANC, mise aux normes, vente immobilière — tout ce que vous devez savoir sur l'assainissement non collectif.", "**L'étude de sol** : Avant toute construction, une étude géotechnique (étude de sol) est vivement recommandée — et même obligatoire dans certains cas (zones argileuses). Elle permet de choisir le bon type de fondations.",
cat: "Assainissement", "OBC Maçonnerie réalise vos fondations avec rigueur, après analyse du terrain. Benoît vous conseille sur la solution la plus adaptée à votre projet dans le Nord.",
date: "20 décembre 2024", ],
readTime: "4 min",
contenu: [
"En France, environ 5 millions de logements sont équipés d'un assainissement non collectif (ANC). Si votre maison n'est pas raccordée au réseau public, vous êtes soumis à des obligations précises.",
"**Le contrôle SPANC** : Le Service Public d'Assainissement Non Collectif peut contrôler votre installation. En cas de non-conformité, vous avez en principe 4 ans pour mettre aux normes, ou moins si vous vendez le bien.",
"**La vente immobilière** : Depuis 2011, un diagnostic d'assainissement est obligatoire lors de toute vente. S'il révèle une non-conformité, l'acheteur doit réaliser les travaux dans l'année suivant l'acte de vente.",
"**Les principales normes** : Votre installation doit traiter correctement les eaux usées avant rejet dans le sol. Les normes imposent une fosse toutes eaux (ou une micro-station) et un dispositif d'épandage adapté à la surface disponible.",
"**OBC Maçonnerie vous accompagne** dans la mise aux normes ou la création de votre système d'assainissement non collectif. Nous intervenons sur Orchies, Douai, Valenciennes et toute la zone.",
],
},
"ossature-bois-avantages": {
titre: "Ossature bois : pourquoi choisir ce mode constructif ?",
description:
"Légèreté, performance thermique, rapidité de construction — l'ossature bois a de nombreux avantages. OBC Maçonnerie vous explique.",
cat: "Construction",
date: "5 novembre 2024",
readTime: "5 min",
contenu: [
"La construction en ossature bois connaît un vrai succès dans le Nord. Et pour cause : ce mode constructif présente de nombreux avantages techniques et économiques.",
"**Performance thermique** : Le bois est un excellent isolant naturel. Une construction ossature bois bien conçue atteint facilement les exigences RE2020.",
"**Rapidité de chantier** : Les éléments préfabriqués permettent de monter les murs en quelques jours. Le clos-couvert est obtenu très rapidement.",
"**Légèreté** : L'ossature bois pèse 5 à 8 fois moins qu'une construction maçonnée, ce qui allège les fondations — un avantage sur les terrains argileux fréquents dans le Nord.",
"**Polyvalence architecturale** : L'ossature bois permet des formes architecturales variées, des larges baies vitrées et une grande liberté de conception.",
"**La combinaison idéale** : OBC Maçonnerie maîtrise la construction ossature bois et la maçonnerie traditionnelle. Benoît vous conseille sur la solution la plus adaptée à votre terrain et vos envies.",
],
},
"travaux-renovation-sans-permis-construction": {
titre: "Quels travaux de rénovation ne nécessitent pas de permis ?",
description:
"Permis de construire, déclaration préalable, simple déclaration — on vous explique les règles selon la nature de vos travaux.",
cat: "Rénovation",
date: "18 octobre 2024",
readTime: "4 min",
contenu: [
"Avant de démarrer des travaux de rénovation, il est important de savoir si vous avez besoin d'une autorisation administrative.",
"**Aucune démarche requise** : Les travaux purement intérieurs (peinture, revêtements, redistribution de cloisons non porteuses, remplacement de fenêtres à l'identique) ne nécessitent généralement aucune démarche.",
"**Déclaration préalable** : Pour les extensions jusqu'à 40 m² (en zone urbaine PLU), les changements de façade, les travaux modifiant l'aspect extérieur.",
"**Permis de construire** : Obligatoire pour les extensions de plus de 40 m², la création d'une surface de plancher supérieure à 20 m² en dehors des zones PLU, ou les changements de destination.",
"**Cas des zones protégées** : Si votre maison est en zone ABF (Architecte des Bâtiments de France), les règles sont plus strictes. Renseignez-vous en mairie.",
"En cas de doute, OBC Maçonnerie vous accompagne dans vos démarches administratives. Benoît connaît bien les règles locales dans le secteur d'Orchies, Douai et Valenciennes.",
],
},
"fondations-maison-quels-types": {
titre: "Les différents types de fondations pour une maison",
description:
"Semelles filantes, radier, pieux — quelles fondations choisir selon votre terrain et votre projet de construction ?",
cat: "Construction",
date: "2 septembre 2024",
readTime: "5 min",
contenu: [
"Les fondations sont la base de toute construction. Mal dimensionnées ou inadaptées au sol, elles peuvent entraîner des désordres graves. Voici les principaux types.",
"**Les semelles filantes** : Le type le plus courant pour les maisons individuelles. Elles répartissent les charges des murs porteurs sur une bande de terrain. Adaptées aux sols stables et homogènes.",
"**Le radier** : Une dalle béton armé qui couvre toute la surface de la maison. Recommandé sur les terrains argileux, instables ou avec présence d'eau. Fréquent dans certains secteurs du Nord.",
"**Les pieux** : Utilisés quand le sol de surface est insuffisant pour porter la maison. Des pieux sont enfoncés jusqu'à une couche de sol plus résistante.",
"**L'étude de sol** : Avant toute construction, une étude géotechnique (étude de sol) est vivement recommandée — et même obligatoire dans certains cas (zones argileuses). Elle permet de choisir le bon type de fondations.",
"OBC Maçonnerie réalise vos fondations avec rigueur, après analyse du terrain. Benoît vous conseille sur la solution la plus adaptée à votre projet dans le Nord.",
],
},
}; };
export async function generateStaticParams() { export async function generateStaticParams() {
return Object.keys(articles).map((slug) => ({ slug })); // FUTURE: utilise getBlogPosts() pour générer les slugs depuis Payload CMS
const posts = await getBlogPosts();
return posts.map((p) => ({ slug: p.slug }));
} }
export async function generateMetadata({ params }: Props): Promise<Metadata> { export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params; const { slug } = await params;
const article = articles[slug]; const [post, config] = await Promise.all([getBlogPost(slug), getSiteConfig()]);
if (!article) return { title: "Article introuvable" }; if (!post) return { title: "Article introuvable" };
return { return {
title: article.titre, title: post.titre,
description: article.description, description: post.extrait,
alternates: { canonical: `https://obc-maconnerie.fr/blog/${slug}` }, alternates: { canonical: `${config.url}/blog/${slug}` },
}; };
} }
export default async function BlogArticlePage({ params }: Props) { export default async function BlogArticlePage({ params }: Props) {
const { slug } = await params; const { slug } = await params;
const article = articles[slug]; const post = await getBlogPost(slug);
if (!article) notFound(); if (!post) notFound();
const contenu = articleContenu[slug] ?? [];
return ( return (
<main id="main-content" className="min-h-screen"> <main id="main-content" className="min-h-screen">
@@ -151,13 +99,13 @@ export default async function BlogArticlePage({ params }: Props) {
</Link> </Link>
<div className="flex items-center gap-3 mb-4"> <div className="flex items-center gap-3 mb-4">
<span className="bg-orange/20 text-orange text-xs font-semibold px-2.5 py-1 rounded-full"> <span className="bg-orange/20 text-orange text-xs font-semibold px-2.5 py-1 rounded-full">
{article.cat} {post.cat}
</span> </span>
<span className="text-white/40 text-xs">{article.date}</span> <span className="text-white/40 text-xs">{post.date}</span>
<span className="text-white/40 text-xs">· {article.readTime} de lecture</span> <span className="text-white/40 text-xs">· {post.readTime} de lecture</span>
</div> </div>
<h1 className="text-2xl md:text-4xl font-bold text-white leading-tight"> <h1 className="text-2xl md:text-4xl font-bold text-white leading-tight">
{article.titre} {post.titre}
</h1> </h1>
</ScrollReveal> </ScrollReveal>
</div> </div>
@@ -178,7 +126,7 @@ export default async function BlogArticlePage({ params }: Props) {
</div> </div>
<div className="space-y-5 text-text leading-relaxed"> <div className="space-y-5 text-text leading-relaxed">
{article.contenu.map((para, i) => ( {contenu.map((para, i) => (
<p key={i} className="text-base text-text-light"> <p key={i} className="text-base text-text-light">
{para.split(/(\*\*[^*]+\*\*)/).map((part, j) => { {para.split(/(\*\*[^*]+\*\*)/).map((part, j) => {
if (part.startsWith("**") && part.endsWith("**")) { if (part.startsWith("**") && part.endsWith("**")) {

View File

@@ -3,74 +3,23 @@ import Link from "next/link";
import Navbar from "@/components/marketing/Navbar"; import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer"; import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal"; import ScrollReveal from "@/components/animations/ScrollReveal";
import { getBlogPosts, getSiteConfig } from "@/lib/content";
export const metadata: Metadata = { export async function generateMetadata(): Promise<Metadata> {
title: "Blog Maçonnerie & Construction | Conseils OBC Maçonnerie", const config = await getSiteConfig();
description: return {
"Conseils, guides et actualités sur la construction de maison, la rénovation et le gros œuvre dans le Nord (59). Blog OBC Maçonnerie par Benoît Colin.", title: "Blog Maçonnerie & Construction | Conseils OBC Maçonnerie",
alternates: { canonical: "https://obc-maconnerie.fr/blog" }, description:
}; "Conseils, guides et actualités sur la construction de maison, la rénovation et le gros œuvre dans le Nord (59). Blog OBC Maçonnerie par Benoît Colin.",
alternates: { canonical: `${config.url}/blog` },
const articles = [ };
{ }
slug: "combien-coute-construction-maison-nord",
titre: "Combien coûte la construction d'une maison dans le Nord en 2025 ?",
extrait:
"Budget, matériaux, terrain, main-d'œuvre — tout ce qu'il faut savoir pour estimer le coût de votre construction neuve dans le Nord.",
cat: "Construction",
date: "15 février 2025",
readTime: "6 min",
},
{
slug: "etapes-renovation-maison-ancienne",
titre: "Les étapes clés d'une rénovation de maison ancienne",
extrait:
"Vous avez acheté une maison ancienne dans le Nord et vous voulez la rénover ? Voici les étapes indispensables pour réussir votre projet.",
cat: "Rénovation",
date: "8 janvier 2025",
readTime: "5 min",
},
{
slug: "assainissement-non-collectif-obligations",
titre: "Assainissement non collectif : vos obligations légales",
extrait:
"Contrôle SPANC, mise aux normes, vente immobilière — tout ce que vous devez savoir sur l'assainissement non collectif.",
cat: "Assainissement",
date: "20 décembre 2024",
readTime: "4 min",
},
{
slug: "ossature-bois-avantages",
titre: "Ossature bois : pourquoi choisir ce mode constructif ?",
extrait:
"Légèreté, performance thermique, rapidité de construction — l'ossature bois a de nombreux avantages. OBC Maçonnerie vous explique.",
cat: "Construction",
date: "5 novembre 2024",
readTime: "5 min",
},
{
slug: "travaux-renovation-sans-permis-construction",
titre: "Quels travaux de rénovation ne nécessitent pas de permis ?",
extrait:
"Permis de construire, déclaration préalable, simple déclaration — on vous explique les règles selon la nature de vos travaux.",
cat: "Rénovation",
date: "18 octobre 2024",
readTime: "4 min",
},
{
slug: "fondations-maison-quels-types",
titre: "Les différents types de fondations pour une maison",
extrait:
"Semelles filantes, radier, pieux — quelles fondations choisir selon votre terrain et votre projet de construction ?",
cat: "Construction",
date: "2 septembre 2024",
readTime: "5 min",
},
];
const cats = ["Tous", "Construction", "Rénovation", "Assainissement"]; const cats = ["Tous", "Construction", "Rénovation", "Assainissement"];
export default function BlogPage() { export default async function BlogPage() {
const articles = await getBlogPosts();
return ( return (
<main id="main-content" className="min-h-screen"> <main id="main-content" className="min-h-screen">
<Navbar /> <Navbar />

View File

@@ -7,7 +7,7 @@ export const metadata: Metadata = {
title: "Conditions Générales de Vente | OBC Maçonnerie", title: "Conditions Générales de Vente | OBC Maçonnerie",
description: description:
"Conditions générales de vente d'OBC Maçonnerie — Benoît Colin, maçon à Mouchin (59310). Prestations de construction, rénovation et gros œuvre.", "Conditions générales de vente d'OBC Maçonnerie — Benoît Colin, maçon à Mouchin (59310). Prestations de construction, rénovation et gros œuvre.",
alternates: { canonical: "https://obc-maconnerie.fr/cgv" }, alternates: { canonical: "https://obc-terrassement.fr/cgv" },
robots: { index: false, follow: false }, robots: { index: false, follow: false },
}; };

View File

@@ -2,12 +2,13 @@ import type { Metadata } from "next";
import Link from "next/link"; import Link from "next/link";
import Navbar from "@/components/marketing/Navbar"; import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer"; import Footer from "@/components/marketing/Footer";
import { siteConfig } from "@/lib/site-config";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Politique de Confidentialité | OBC Maçonnerie", title: "Politique de Confidentialité | OBC Maçonnerie",
description: description:
"Politique de confidentialité et protection des données personnelles du site OBC Maçonnerie, conformément au RGPD.", "Politique de confidentialité et protection des données personnelles du site OBC Maçonnerie, conformément au RGPD.",
alternates: { canonical: "https://obc-maconnerie.fr/confidentialite" }, alternates: { canonical: "https://obc-terrassement.fr/confidentialite" },
robots: { index: false, follow: false }, robots: { index: false, follow: false },
}; };
@@ -34,8 +35,8 @@ export default function Confidentialite() {
<p><strong className="text-text">Benoît COLIN OBC Maçonnerie</strong></p> <p><strong className="text-text">Benoît COLIN OBC Maçonnerie</strong></p>
<p>SIREN : 531 827 871</p> <p>SIREN : 531 827 871</p>
<p>221 Route de Saint-Amand, 59310 Mouchin</p> <p>221 Route de Saint-Amand, 59310 Mouchin</p>
<p>Tél : <a href="tel:0674453089" className="text-orange hover:underline">06 74 45 30 89</a></p> <p>Tél : <a href={`tel:${siteConfig.phoneRaw}`} className="text-orange hover:underline">{siteConfig.phone}</a></p>
<p>Email : <a href="mailto:contact@obc-maconnerie.fr" className="text-orange hover:underline">contact@obc-maconnerie.fr</a></p> <p>Email : <a href="mailto:contact@obc-terrassement.fr" className="text-orange hover:underline">contact@obc-terrassement.fr</a></p>
</div> </div>
</section> </section>
@@ -100,8 +101,8 @@ export default function Confidentialite() {
Conformément au RGPD, vous disposez d&apos;un droit d&apos;accès, de rectification, d&apos;effacement et de portabilité de vos données. Pour exercer ces droits, contactez-nous : Conformément au RGPD, vous disposez d&apos;un droit d&apos;accès, de rectification, d&apos;effacement et de portabilité de vos données. Pour exercer ces droits, contactez-nous :
</p> </p>
<p className="mt-3"> <p className="mt-3">
<a href="mailto:contact@obc-maconnerie.fr" className="text-orange font-semibold hover:underline"> <a href="mailto:contact@obc-terrassement.fr" className="text-orange font-semibold hover:underline">
contact@obc-maconnerie.fr contact@obc-terrassement.fr
</a> </a>
</p> </p>
</section> </section>

View File

@@ -6,7 +6,7 @@ export const metadata: Metadata = {
description: description:
"OBC Maçonnerie intervient à Douai pour vos travaux de construction de maison, rénovation et gros œuvre. Benoît Colin, maçon expert. Devis gratuit.", "OBC Maçonnerie intervient à Douai pour vos travaux de construction de maison, rénovation et gros œuvre. Benoît Colin, maçon expert. Devis gratuit.",
keywords: ["construction maison Douai", "maçon Douai", "rénovation Douai", "gros oeuvre Douai", "maçon rénovation Douai"], keywords: ["construction maison Douai", "maçon Douai", "rénovation Douai", "gros oeuvre Douai", "maçon rénovation Douai"],
alternates: { canonical: "https://obc-maconnerie.fr/construction-maison-douai" }, alternates: { canonical: "https://obc-terrassement.fr/construction-maison-douai" },
}; };
export default function ConstructionMaisonDouaiPage() { export default function ConstructionMaisonDouaiPage() {

View File

@@ -6,7 +6,7 @@ export const metadata: Metadata = {
description: description:
"OBC Maçonnerie intervient à Orchies pour vos travaux de construction de maison, rénovation et gros œuvre. Benoît Colin, maçon expert. Devis gratuit.", "OBC Maçonnerie intervient à Orchies pour vos travaux de construction de maison, rénovation et gros œuvre. Benoît Colin, maçon expert. Devis gratuit.",
keywords: ["construction maison Orchies", "maçon Orchies", "rénovation Orchies", "gros oeuvre Orchies"], keywords: ["construction maison Orchies", "maçon Orchies", "rénovation Orchies", "gros oeuvre Orchies"],
alternates: { canonical: "https://obc-maconnerie.fr/construction-maison-orchies" }, alternates: { canonical: "https://obc-terrassement.fr/construction-maison-orchies" },
}; };
export default function ConstructionMaisonOrchiesPage() { export default function ConstructionMaisonOrchiesPage() {

View File

@@ -6,7 +6,7 @@ export const metadata: Metadata = {
description: description:
"OBC Maçonnerie intervient à Valenciennes pour vos travaux de construction de maison, rénovation et gros œuvre. Benoît Colin, maçon expert. Devis gratuit.", "OBC Maçonnerie intervient à Valenciennes pour vos travaux de construction de maison, rénovation et gros œuvre. Benoît Colin, maçon expert. Devis gratuit.",
keywords: ["construction maison Valenciennes", "maçon Valenciennes", "rénovation Valenciennes", "gros oeuvre Valenciennes"], keywords: ["construction maison Valenciennes", "maçon Valenciennes", "rénovation Valenciennes", "gros oeuvre Valenciennes"],
alternates: { canonical: "https://obc-maconnerie.fr/construction-maison-valenciennes" }, alternates: { canonical: "https://obc-terrassement.fr/construction-maison-valenciennes" },
}; };
export default function ConstructionMaisonValenciennesPage() { export default function ConstructionMaisonValenciennesPage() {

View File

@@ -4,8 +4,12 @@ import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer"; import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal"; import ScrollReveal from "@/components/animations/ScrollReveal";
import ContactForm from "@/components/marketing/ContactForm"; import ContactForm from "@/components/marketing/ContactForm";
import { getSiteConfig } from "@/lib/content";
export async function generateMetadata(): Promise<Metadata> {
const config = await getSiteConfig();
return {
export const metadata: Metadata = {
title: "Construction de Maison dans le Nord | OBC Maçonnerie Orchies", title: "Construction de Maison dans le Nord | OBC Maçonnerie Orchies",
description: description:
"Construction neuve, fondations, ossature bois dans le Nord (59). OBC Maçonnerie vous accompagne de A à Z. Devis gratuit.", "Construction neuve, fondations, ossature bois dans le Nord (59). OBC Maçonnerie vous accompagne de A à Z. Devis gratuit.",
@@ -17,8 +21,9 @@ export const metadata: Metadata = {
"construction maison Douai", "construction maison Douai",
"construction maison Valenciennes", "construction maison Valenciennes",
], ],
alternates: { canonical: "https://obc-maconnerie.fr/construction-maison" }, alternates: { canonical: `${config.url}/construction-maison` },
}; };
}
const etapes = [ const etapes = [
{ num: "01", title: "Étude & conseil", desc: "Benoît analyse votre terrain, votre plan et vos envies. Il adapte si besoin les plans d'architecte et vous conseille sur les matériaux." }, { num: "01", title: "Étude & conseil", desc: "Benoît analyse votre terrain, votre plan et vos envies. Il adapte si besoin les plans d'architecte et vous conseille sur les matériaux." },
@@ -29,7 +34,9 @@ const etapes = [
{ num: "06", title: "Remise des clés", desc: "Livraison de votre maison dans les délais convenus, avec un chantier propre et soigné." }, { num: "06", title: "Remise des clés", desc: "Livraison de votre maison dans les délais convenus, avec un chantier propre et soigné." },
]; ];
export default function ConstructionMaisonPage() { export default async function ConstructionMaisonPage() {
const config = await getSiteConfig();
const { phone, phoneRaw } = config;
return ( return (
<main id="main-content" className="min-h-screen"> <main id="main-content" className="min-h-screen">
<Navbar /> <Navbar />
@@ -56,9 +63,7 @@ export default function ConstructionMaisonPage() {
<Link href="/contact" className="inline-flex items-center justify-center gap-2 bg-orange hover:bg-orange-hover text-white font-bold px-7 py-3.5 rounded-xl transition-colors pulse-glow"> <Link href="/contact" className="inline-flex items-center justify-center gap-2 bg-orange hover:bg-orange-hover text-white font-bold px-7 py-3.5 rounded-xl transition-colors pulse-glow">
Demander un devis gratuit Demander un devis gratuit
</Link> </Link>
<a href="tel:0674453089" className="inline-flex items-center justify-center gap-2 bg-white/10 hover:bg-white/20 text-white font-semibold px-7 py-3.5 rounded-xl transition-colors border border-white/20"> <a href={`tel:${phoneRaw}`} className="inline-flex items-center justify-center gap-2 bg-white/10 hover:bg-white/20 text-white font-semibold px-7 py-3.5 rounded-xl transition-colors border border-white/20">{phone}</a>
06 74 45 30 89
</a>
</div> </div>
</ScrollReveal> </ScrollReveal>
</div> </div>

View File

@@ -3,50 +3,46 @@ import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer"; import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal"; import ScrollReveal from "@/components/animations/ScrollReveal";
import ContactForm from "@/components/marketing/ContactForm"; import ContactForm from "@/components/marketing/ContactForm";
import { getSiteConfig } from "@/lib/content";
export const metadata: Metadata = { export async function generateMetadata(): Promise<Metadata> {
title: "Contact & Devis Gratuit | OBC Maçonnerie Nord", const config = await getSiteConfig();
description: return {
"Contactez OBC Maçonnerie pour un devis gratuit. Benoît Colin intervient à Orchies, Douai, Valenciennes et dans un rayon de 30km autour de Mouchin (59).", title: "Contact & Devis Gratuit | OBC Maçonnerie Nord",
alternates: { canonical: "https://obc-maconnerie.fr/contact" }, description:
}; "Contactez OBC Maçonnerie pour un devis gratuit. Benoît Colin intervient à Orchies, Douai, Valenciennes et dans un rayon de 30km autour de Mouchin (59).",
alternates: { canonical: `${config.url}/contact` },
};
}
const infos = [ export default async function ContactPage() {
{ const config = await getSiteConfig();
icon: "📞", const { phone, phoneRaw, email, address, zones, zoneDescription } = config;
titre: "Téléphone",
val: "06 74 45 30 89",
href: "tel:0674453089",
desc: "LunVen 7h19h",
},
{
icon: "📍",
titre: "Adresse",
val: "221 Route de Saint-Amand, 59310 Mouchin",
href: undefined,
desc: "Rayon d'intervention : 30km",
},
{
icon: "📧",
titre: "Email",
val: "contact@obc-maconnerie.fr",
href: "mailto:contact@obc-maconnerie.fr",
desc: "Réponse sous 24h",
},
];
const zones = [ const infos = [
"Orchies", {
"Mouchin", icon: "📞",
"Flines-lès-Raches", titre: "Téléphone",
"Château-l'Abbaye", val: phone,
"Mérignies", href: `tel:${phoneRaw}`,
"Douai", desc: "LunVen 7h19h",
"Valenciennes", },
"Saint-Amand-les-Eaux", {
]; icon: "📍",
titre: "Adresse",
val: address,
href: undefined as string | undefined,
desc: "Rayon d'intervention : 30km",
},
{
icon: "📧",
titre: "Email",
val: email,
href: `mailto:${email}`,
desc: "Réponse sous 24h",
},
];
export default function ContactPage() {
return ( return (
<main id="main-content" className="min-h-screen"> <main id="main-content" className="min-h-screen">
<Navbar /> <Navbar />
@@ -100,11 +96,11 @@ export default function ContactPage() {
))} ))}
</div> </div>
<p className="text-text-muted text-xs italic"> <p className="text-text-muted text-xs italic">
Et toutes les communes dans un rayon de 20-30 km autour de Mouchin (Nord 59). Et toutes les communes dans un rayon de {zoneDescription}.
</p> </p>
<div className="mt-8 bg-navy rounded-2xl p-6"> <div className="mt-8 bg-navy rounded-2xl p-6">
<h3 className="text-white font-bold mb-2">Devis gratuit & sans engagement</h3> <h3 className="text-white font-bold mb-2">Devis gratuit &amp; sans engagement</h3>
<p className="text-white/60 text-sm"> <p className="text-white/60 text-sm">
Benoît se déplace sur votre chantier pour évaluer votre projet, vous conseiller et vous remettre un devis clair et détaillé. Gratuit et sans engagement. Benoît se déplace sur votre chantier pour évaluer votre projet, vous conseiller et vous remettre un devis clair et détaillé. Gratuit et sans engagement.
</p> </p>

View File

@@ -4,8 +4,12 @@ import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer"; import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal"; import ScrollReveal from "@/components/animations/ScrollReveal";
import ContactForm from "@/components/marketing/ContactForm"; import ContactForm from "@/components/marketing/ContactForm";
import { getSiteConfig } from "@/lib/content";
export async function generateMetadata(): Promise<Metadata> {
const config = await getSiteConfig();
return {
export const metadata: Metadata = {
title: "Création d'Accès, Voiries & Entrées | OBC Maçonnerie Nord", title: "Création d'Accès, Voiries & Entrées | OBC Maçonnerie Nord",
description: description:
"Création d'accès, voiries privées, entrées de propriété et chemins dans le Nord (59). OBC Maçonnerie à Orchies. Devis gratuit.", "Création d'accès, voiries privées, entrées de propriété et chemins dans le Nord (59). OBC Maçonnerie à Orchies. Devis gratuit.",
@@ -17,8 +21,9 @@ export const metadata: Metadata = {
"béton imprimé Nord", "béton imprimé Nord",
"création accès Orchies", "création accès Orchies",
], ],
alternates: { canonical: "https://obc-maconnerie.fr/creation-acces" }, alternates: { canonical: `${config.url}/creation-acces` },
}; };
}
const types = [ const types = [
{ icon: "🚗", title: "Entrées de propriété", desc: "Création d'une entrée soignée en béton, béton imprimé, pavés ou gravier stabilisé — adaptée à votre maison." }, { icon: "🚗", title: "Entrées de propriété", desc: "Création d'une entrée soignée en béton, béton imprimé, pavés ou gravier stabilisé — adaptée à votre maison." },
@@ -29,7 +34,9 @@ const types = [
{ icon: "💧", title: "Drainage & évacuation", desc: "Mise en place de caniveaux, avaloirs et systèmes de drainage pour éviter les accumulations d'eau." }, { icon: "💧", title: "Drainage & évacuation", desc: "Mise en place de caniveaux, avaloirs et systèmes de drainage pour éviter les accumulations d'eau." },
]; ];
export default function CreationAccesPage() { export default async function CreationAccesPage() {
const config = await getSiteConfig();
const { phone, phoneRaw } = config;
return ( return (
<main id="main-content" className="min-h-screen"> <main id="main-content" className="min-h-screen">
<Navbar /> <Navbar />
@@ -55,9 +62,7 @@ export default function CreationAccesPage() {
<Link href="/contact" className="inline-flex items-center justify-center gap-2 bg-orange hover:bg-orange-hover text-white font-bold px-7 py-3.5 rounded-xl transition-colors pulse-glow"> <Link href="/contact" className="inline-flex items-center justify-center gap-2 bg-orange hover:bg-orange-hover text-white font-bold px-7 py-3.5 rounded-xl transition-colors pulse-glow">
Demander un devis gratuit Demander un devis gratuit
</Link> </Link>
<a href="tel:0674453089" className="inline-flex items-center justify-center gap-2 bg-white/10 hover:bg-white/20 text-white font-semibold px-7 py-3.5 rounded-xl transition-colors border border-white/20"> <a href={`tel:${phoneRaw}`} className="inline-flex items-center justify-center gap-2 bg-white/10 hover:bg-white/20 text-white font-semibold px-7 py-3.5 rounded-xl transition-colors border border-white/20">{phone}</a>
06 74 45 30 89
</a>
</div> </div>
</ScrollReveal> </ScrollReveal>
</div> </div>

View File

@@ -4,8 +4,12 @@ import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer"; import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal"; import ScrollReveal from "@/components/animations/ScrollReveal";
import ContactForm from "@/components/marketing/ContactForm"; import ContactForm from "@/components/marketing/ContactForm";
import { getSiteConfig } from "@/lib/content";
export async function generateMetadata(): Promise<Metadata> {
const config = await getSiteConfig();
return {
export const metadata: Metadata = {
title: "Démolition Maison Nord 59 | OBC Maçonnerie", title: "Démolition Maison Nord 59 | OBC Maçonnerie",
description: description:
"Démolition totale ou partielle de maison, murs porteurs, bâtiments dans le Nord (59). OBC Maçonnerie à Orchies. Toutes garanties de sécurité. Devis gratuit.", "Démolition totale ou partielle de maison, murs porteurs, bâtiments dans le Nord (59). OBC Maçonnerie à Orchies. Toutes garanties de sécurité. Devis gratuit.",
@@ -17,8 +21,9 @@ export const metadata: Metadata = {
"démolition Orchies", "démolition Orchies",
"démolition Douai", "démolition Douai",
], ],
alternates: { canonical: "https://obc-maconnerie.fr/demolition" }, alternates: { canonical: `${config.url}/demolition` },
}; };
}
const types = [ const types = [
{ icon: "🏚️", title: "Démolition totale", desc: "Destruction complète d'un bâtiment résidentiel ou annexe, avec évacuation des gravats et remise en état du terrain." }, { icon: "🏚️", title: "Démolition totale", desc: "Destruction complète d'un bâtiment résidentiel ou annexe, avec évacuation des gravats et remise en état du terrain." },
@@ -29,7 +34,9 @@ const types = [
{ icon: "🏠", title: "Curage intérieur", desc: "Enlèvement complet des éléments intérieurs (cloisons, planchers, revêtements) avant une rénovation lourde." }, { icon: "🏠", title: "Curage intérieur", desc: "Enlèvement complet des éléments intérieurs (cloisons, planchers, revêtements) avant une rénovation lourde." },
]; ];
export default function DemolitionPage() { export default async function DemolitionPage() {
const config = await getSiteConfig();
const { phone, phoneRaw } = config;
return ( return (
<main id="main-content" className="min-h-screen"> <main id="main-content" className="min-h-screen">
<Navbar /> <Navbar />
@@ -55,9 +62,7 @@ export default function DemolitionPage() {
<Link href="/contact" className="inline-flex items-center justify-center gap-2 bg-orange hover:bg-orange-hover text-white font-bold px-7 py-3.5 rounded-xl transition-colors pulse-glow"> <Link href="/contact" className="inline-flex items-center justify-center gap-2 bg-orange hover:bg-orange-hover text-white font-bold px-7 py-3.5 rounded-xl transition-colors pulse-glow">
Demander un devis gratuit Demander un devis gratuit
</Link> </Link>
<a href="tel:0674453089" className="inline-flex items-center justify-center gap-2 bg-white/10 hover:bg-white/20 text-white font-semibold px-7 py-3.5 rounded-xl transition-colors border border-white/20"> <a href={`tel:${phoneRaw}`} className="inline-flex items-center justify-center gap-2 bg-white/10 hover:bg-white/20 text-white font-semibold px-7 py-3.5 rounded-xl transition-colors border border-white/20">{phone}</a>
06 74 45 30 89
</a>
</div> </div>
</ScrollReveal> </ScrollReveal>
</div> </div>

View File

@@ -1,8 +1,9 @@
import type { Metadata } from "next"; import type { Metadata } from "next";
import CookieBanner from "@/components/CookieBanner"; import CookieBanner from "@/components/CookieBanner";
import { siteConfig } from "@/lib/site-config";
import "./globals.css"; import "./globals.css";
const BASE_URL = process.env.NEXT_PUBLIC_APP_URL || "https://obc-maconnerie.fr"; const BASE_URL = process.env.NEXT_PUBLIC_APP_URL || siteConfig.url;
export const metadata: Metadata = { export const metadata: Metadata = {
metadataBase: new URL(BASE_URL), metadataBase: new URL(BASE_URL),
@@ -95,8 +96,8 @@ export default function RootLayout({
name: "OBC Maçonnerie", name: "OBC Maçonnerie",
description: description:
"Construction de maison, rénovation, assainissement et gros œuvre dans le Nord", "Construction de maison, rénovation, assainissement et gros œuvre dans le Nord",
telephone: "06 74 45 30 89", telephone: siteConfig.phone,
email: "contact@obc-maconnerie.fr", email: siteConfig.email,
url: BASE_URL, url: BASE_URL,
logo: `${BASE_URL}/icon-512.svg`, logo: `${BASE_URL}/icon-512.svg`,
image: `${BASE_URL}/og-image.jpg`, image: `${BASE_URL}/og-image.jpg`,
@@ -143,7 +144,7 @@ export default function RootLayout({
}, },
contactPoint: { contactPoint: {
"@type": "ContactPoint", "@type": "ContactPoint",
telephone: "06 74 45 30 89", telephone: siteConfig.phone,
contactType: "customer service", contactType: "customer service",
availableLanguage: "French", availableLanguage: "French",
}, },

View File

@@ -6,7 +6,7 @@ export const metadata: Metadata = {
description: description:
"OBC Maçonnerie intervient à Flines-lès-Raches pour vos travaux de construction, rénovation et gros œuvre. Benoît Colin, maçon expert. Devis gratuit.", "OBC Maçonnerie intervient à Flines-lès-Raches pour vos travaux de construction, rénovation et gros œuvre. Benoît Colin, maçon expert. Devis gratuit.",
keywords: ["maçon Flines-lès-Raches", "construction Flines Raches", "rénovation Flines Raches", "maçon Flines Nord"], keywords: ["maçon Flines-lès-Raches", "construction Flines Raches", "rénovation Flines Raches", "maçon Flines Nord"],
alternates: { canonical: "https://obc-maconnerie.fr/macon-flines-lez-raches" }, alternates: { canonical: "https://obc-terrassement.fr/macon-flines-lez-raches" },
}; };
export default function MaconFlinesPage() { export default function MaconFlinesPage() {

View File

@@ -6,7 +6,7 @@ export const metadata: Metadata = {
description: description:
"OBC Maçonnerie est basée à Mouchin (59310). Benoît Colin, maçon expert local. Construction, rénovation, assainissement, gros œuvre. Devis gratuit.", "OBC Maçonnerie est basée à Mouchin (59310). Benoît Colin, maçon expert local. Construction, rénovation, assainissement, gros œuvre. Devis gratuit.",
keywords: ["maçon Mouchin", "entreprise maçonnerie Mouchin", "construction Mouchin", "rénovation Mouchin"], keywords: ["maçon Mouchin", "entreprise maçonnerie Mouchin", "construction Mouchin", "rénovation Mouchin"],
alternates: { canonical: "https://obc-maconnerie.fr/macon-mouchin" }, alternates: { canonical: "https://obc-terrassement.fr/macon-mouchin" },
}; };
export default function MaconMouchinPage() { export default function MaconMouchinPage() {

View File

@@ -6,7 +6,7 @@ export const metadata: Metadata = {
description: description:
"OBC Maçonnerie intervient à Saint-Amand-les-Eaux pour vos travaux de construction, rénovation, assainissement et gros œuvre. Devis gratuit.", "OBC Maçonnerie intervient à Saint-Amand-les-Eaux pour vos travaux de construction, rénovation, assainissement et gros œuvre. Devis gratuit.",
keywords: ["maçon Saint-Amand-les-Eaux", "construction Saint-Amand", "rénovation Saint-Amand les Eaux", "maçon Saint-Amand Nord"], keywords: ["maçon Saint-Amand-les-Eaux", "construction Saint-Amand", "rénovation Saint-Amand les Eaux", "maçon Saint-Amand Nord"],
alternates: { canonical: "https://obc-maconnerie.fr/macon-saint-amand-les-eaux" }, alternates: { canonical: "https://obc-terrassement.fr/macon-saint-amand-les-eaux" },
}; };
export default function MaconSaintAmandPage() { export default function MaconSaintAmandPage() {

View File

@@ -2,12 +2,13 @@ import type { Metadata } from "next";
import Link from "next/link"; import Link from "next/link";
import Navbar from "@/components/marketing/Navbar"; import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer"; import Footer from "@/components/marketing/Footer";
import { siteConfig } from "@/lib/site-config";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Mentions Légales | OBC Maçonnerie", title: "Mentions Légales | OBC Maçonnerie",
description: description:
"Mentions légales du site OBC Maçonnerie — Benoît Colin, maçon à Mouchin (59310). SIREN 531 827 871.", "Mentions légales du site OBC Maçonnerie — Benoît Colin, maçon à Mouchin (59310). SIREN 531 827 871.",
alternates: { canonical: "https://obc-maconnerie.fr/mentions-legales" }, alternates: { canonical: "https://obc-terrassement.fr/mentions-legales" },
robots: { index: false, follow: false }, robots: { index: false, follow: false },
}; };
@@ -31,7 +32,7 @@ export default function MentionsLegales() {
<div className="space-y-10 text-text-light text-sm leading-relaxed"> <div className="space-y-10 text-text-light text-sm leading-relaxed">
<p className="italic text-text-muted"> <p className="italic text-text-muted">
Conformément aux dispositions de la loi n° 2004-575 du 21 juin 2004 pour la confiance en l&apos;économie numérique (LCEN), voici les informations légales du site <strong className="text-text">obc-maconnerie.fr</strong>. Conformément aux dispositions de la loi n° 2004-575 du 21 juin 2004 pour la confiance en l&apos;économie numérique (LCEN), voici les informations légales du site <strong className="text-text">obc-terrassement.fr</strong>.
</p> </p>
<section> <section>
@@ -43,8 +44,8 @@ export default function MentionsLegales() {
<li><strong className="text-text">Statut :</strong> Entreprise individuelle</li> <li><strong className="text-text">Statut :</strong> Entreprise individuelle</li>
<li><strong className="text-text">SIREN :</strong> 531 827 871</li> <li><strong className="text-text">SIREN :</strong> 531 827 871</li>
<li><strong className="text-text">Siège social :</strong> 221 Route de Saint-Amand, 59310 Mouchin, France</li> <li><strong className="text-text">Siège social :</strong> 221 Route de Saint-Amand, 59310 Mouchin, France</li>
<li><strong className="text-text">Téléphone :</strong> <a href="tel:0674453089" className="text-orange hover:underline">06 74 45 30 89</a></li> <li><strong className="text-text">Téléphone :</strong> <a href={`tel:${siteConfig.phoneRaw}`} className="text-orange hover:underline">{siteConfig.phone}</a></li>
<li><strong className="text-text">Email :</strong> <a href="mailto:contact@obc-maconnerie.fr" className="text-orange hover:underline">contact@obc-maconnerie.fr</a></li> <li><strong className="text-text">Email :</strong> <a href="mailto:contact@obc-terrassement.fr" className="text-orange hover:underline">contact@obc-terrassement.fr</a></li>
</ul> </ul>
<p className="mt-3"><strong className="text-text">Directeur de la publication :</strong> Benoît COLIN</p> <p className="mt-3"><strong className="text-text">Directeur de la publication :</strong> Benoît COLIN</p>
</div> </div>

View File

@@ -4,181 +4,53 @@ import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer"; import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal"; import ScrollReveal from "@/components/animations/ScrollReveal";
import ContactForm from "@/components/marketing/ContactForm"; import ContactForm from "@/components/marketing/ContactForm";
import {
getSiteConfig,
getServices,
getTestimonials,
getFAQ,
getValues,
getPartners,
getRealisations,
} from "@/lib/content";
import type { Service, Testimonial, FAQItem } from "@/types/content";
export const metadata: Metadata = { export async function generateMetadata(): Promise<Metadata> {
title: "OBC Maçonnerie | Constructeur & Maçon à Orchies (Nord 59)", const config = await getSiteConfig();
description: return {
"Benoît Colin, maçon expert à Mouchin. Construction de maison, rénovation, assainissement et gros œuvre dans un rayon de 30km autour d'Orchies. Devis gratuit.", title: config.seo.title,
alternates: { description: config.seo.description,
canonical: "https://obc-maconnerie.fr", alternates: { canonical: config.url },
}, };
}; }
const services = [ function ServiceCard({ service }: { service: Service }) {
{ const href = service.slug === "conseil" ? "/contact" : `/${service.slug}`;
icon: "🏠", return (
title: "Construction de maison", <Link
desc: "Fondations, ossature bois, gros œuvre — on bâtit votre projet de A à Z avec vous.", href={href}
href: "/construction-maison", className="group block bg-bg-white border border-border rounded-2xl p-6 hover:border-orange hover:shadow-lg transition-all duration-300 card-hover"
}, >
{ <div className="text-3xl mb-4">{service.icon}</div>
icon: "🔨", <h3 className="text-navy font-bold text-lg mb-2 group-hover:text-orange transition-colors">
title: "Rénovation", {service.title}
desc: "Maison ou appartement, on s'adapte à votre projet et vos envies.", </h3>
href: "/renovation", <p className="text-text-light text-sm leading-relaxed">{service.shortDescription}</p>
}, <div className="mt-4 flex items-center gap-1 text-orange text-sm font-semibold">
{ En savoir plus
icon: "💧", <svg className="w-4 h-4 group-hover:translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
title: "Assainissement", <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
desc: "Mise aux normes et création de systèmes d'assainissement fiables.", </svg>
href: "/assainissement", </div>
}, </Link>
{ );
icon: "🚧", }
title: "Création d'accès",
desc: "Voiries, entrées, chemins — on crée vos accès sur mesure.",
href: "/creation-acces",
},
{
icon: "🏗️",
title: "Démolition",
desc: "Démolition totale ou partielle, avec toutes les garanties de sécurité.",
href: "/demolition",
},
{
icon: "🤝",
title: "Conseil & Accompagnement",
desc: "Benoît vous éclaire dans vos choix : matériaux, plans, adaptations — on réfléchit ensemble.",
href: "/contact",
},
];
const pilliers = [
{
icon: "📍",
title: "Proche de vous",
desc: "Disponible, à l'écoute, Benoît intervient dans votre secteur local et prend le temps de comprendre votre projet.",
},
{
icon: "💡",
title: "Conseil expert",
desc: "Il guide vos choix de matériaux et adapte les plans d'architecte pour un résultat qui vous ressemble.",
},
{
icon: "🛡️",
title: "Acteur de confiance",
desc: "Transparent à chaque étape, Benoît rassure, explique et vous tient informé de l'avancement du chantier.",
},
{
icon: "❤️",
title: "Passionné du métier",
desc: "\"On ne fait jamais deux fois la même maison.\" Benoît aime être au cœur de chaque projet, de A à Z.",
},
];
const partenaires = [
{ label: "Électricité", icon: "⚡" },
{ label: "Plomberie", icon: "🔧" },
{ label: "Charpente", icon: "🪵" },
{ label: "Couverture", icon: "🏚️" },
{ label: "Isolation", icon: "🧱" },
{ label: "Menuiserie", icon: "🚪" },
{ label: "Carrelage", icon: "🔳" },
{ label: "Peinture", icon: "🎨" },
];
const villes = [
"Orchies",
"Mouchin",
"Flines-lès-Raches",
"Château-l'Abbaye",
"Mérignies",
"Douai",
"Valenciennes",
"Saint-Amand-les-Eaux",
];
const realisations = [
{
title: "Construction d'une maison individuelle",
desc: "Fondations, gros œuvre et ossature — livraison clé en main à Orchies.",
cat: "Construction neuve",
color: "bg-navy",
},
{
title: "Rénovation complète d'une maison de ville",
desc: "Restructuration intérieure, cloisons, escalier réhabilité à Douai.",
cat: "Rénovation",
color: "bg-stone",
},
{
title: "Création d'un accès et chemin d'entrée",
desc: "Voirie et entrée béton imprimé, aménagement paysager à Mérignies.",
cat: "Création d'accès",
color: "bg-orange",
},
];
const temoignages = [
{
nom: "Christophe & Marie L.",
lieu: "Orchies",
projet: "Construction maison",
texte:
"Benoît nous a accompagnés de A à Z dans la construction de notre maison. Il a su adapter le plan d'architecte à nos envies tout en respectant notre budget. Disponible, professionnel, et vraiment à l'écoute. On recommande les yeux fermés.",
note: 5,
},
{
nom: "Sophie D.",
lieu: "Douai",
projet: "Rénovation",
texte:
"On lui a confié la rénovation complète de notre maison de 1970. Benoît a pris le temps de tout nous expliquer, a proposé des solutions auxquelles on n'avait pas pensé, et le résultat est magnifique. Un vrai professionnel.",
note: 5,
},
{
nom: "Famille Moreau",
lieu: "Saint-Amand-les-Eaux",
projet: "Assainissement",
texte:
"Mise aux normes de notre système d'assainissement réalisée dans les délais et en toute transparence. Benoît nous a expliqué chaque étape. Très sérieux et propre dans son travail.",
note: 5,
},
];
const faqs = [
{
q: "Dans quelle zone intervenez-vous ?",
a: "OBC Maçonnerie intervient dans un rayon de 20 à 30 km autour de Mouchin (59310) : Orchies, Flines-lès-Raches, Château-l'Abbaye, Mérignies, Douai, Valenciennes, Saint-Amand-les-Eaux et les communes alentour.",
},
{
q: "Faites-vous des devis gratuits ?",
a: "Oui, absolument. Le devis est gratuit et sans engagement. Contactez Benoît par téléphone ou via le formulaire, il se déplace pour évaluer votre projet.",
},
{
q: "Pouvez-vous adapter un plan d'architecte ?",
a: "Oui, c'est même l'une de nos spécialités. Benoît collabore directement avec vous pour adapter les plans à vos envies, votre budget et les contraintes du terrain.",
},
{
q: "Combien de temps dure une construction de maison ?",
a: "Une construction neuve prend en moyenne 10 à 18 mois selon la complexité du projet, les conditions météo et les délais de livraison des matériaux. Benoît vous donne un calendrier dès la signature.",
},
{
q: "Travaillez-vous avec d'autres artisans ?",
a: "Oui. OBC Maçonnerie dispose d'un réseau de partenaires de confiance pour tous les corps de métier : électricité, plomberie, charpente, isolation, menuiserie, carrelage, peinture et couverture. Vous avez un seul interlocuteur pour coordonner l'ensemble.",
},
];
function StarRating({ note }: { note: number }) { function StarRating({ note }: { note: number }) {
return ( return (
<div className="flex gap-0.5"> <div className="flex gap-0.5">
{Array.from({ length: 5 }).map((_, i) => ( {Array.from({ length: 5 }).map((_, i) => (
<svg <svg key={i} className={`w-4 h-4 ${i < note ? "text-orange" : "text-border"}`} fill="currentColor" viewBox="0 0 20 20">
key={i}
className={`w-4 h-4 ${i < note ? "text-orange" : "text-border"}`}
fill="currentColor"
viewBox="0 0 20 20"
>
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" /> <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
</svg> </svg>
))} ))}
@@ -186,68 +58,90 @@ function StarRating({ note }: { note: number }) {
); );
} }
export default function HomePage() { function TestimonialCard({ t }: { t: Testimonial }) {
const serviceLabel: Record<string, string> = {
"construction-maison": "Construction de maison",
renovation: "Rénovation",
assainissement: "Assainissement",
"creation-acces": "Création d'accès",
demolition: "Démolition",
};
return (
<div className="bg-white/5 border border-white/10 rounded-2xl p-6 h-full flex flex-col">
<StarRating note={t.rating} />
<p className="text-white/80 text-sm leading-relaxed mt-4 flex-1 italic">&ldquo;{t.text}&rdquo;</p>
<div className="mt-5 pt-4 border-t border-white/10">
<p className="text-white font-semibold text-sm">{t.name}</p>
<p className="text-white/40 text-xs">{t.ville} {serviceLabel[t.service] ?? t.service}</p>
</div>
</div>
);
}
function FAQAccordion({ item }: { item: FAQItem }) {
return (
<details className="group bg-bg-white border border-border rounded-2xl overflow-hidden">
<summary className="flex items-center justify-between px-6 py-4 cursor-pointer font-semibold text-navy hover:text-orange transition-colors list-none">
{item.question}
<svg className="w-5 h-5 text-text-muted group-open:rotate-180 transition-transform shrink-0 ml-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</summary>
<div className="px-6 pb-5 text-text-light text-sm leading-relaxed border-t border-border-light pt-4">
{item.answer}
</div>
</details>
);
}
export default async function HomePage() {
const [config, services, testimonials, faqItems, values, partners, realisations] =
await Promise.all([
getSiteConfig(),
getServices(),
getTestimonials(),
getFAQ(),
getValues(),
getPartners(),
getRealisations(),
]);
const { hero, zones, zoneDescription, partnersTitle, partnersMessage, phone, phoneRaw } = config;
return ( return (
<main id="main-content" className="min-h-screen"> <main id="main-content" className="min-h-screen">
<Navbar /> <Navbar />
{/* ── SECTION 1 — HERO ── */} {/* ── SECTION 1 — HERO ── */}
<section className="relative bg-navy overflow-hidden pt-20 pb-24 md:pt-28 md:pb-32"> <section className="relative bg-navy overflow-hidden pt-20 pb-24 md:pt-28 md:pb-32">
{/* Background texture */} <div className="absolute inset-0 opacity-5" style={{ backgroundImage: "repeating-linear-gradient(45deg,#fff 0,#fff 1px,transparent 0,transparent 50%)", backgroundSize: "20px 20px" }} />
<div className="absolute inset-0 opacity-5">
<div
className="absolute inset-0"
style={{
backgroundImage:
"repeating-linear-gradient(45deg, #fff 0, #fff 1px, transparent 0, transparent 50%)",
backgroundSize: "20px 20px",
}}
/>
</div>
<div className="relative max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 text-center"> <div className="relative max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
{/* Badge */} <div className="inline-flex items-center gap-2 bg-white/10 border border-white/20 rounded-full px-4 py-1.5 mb-6">
<div className="inline-flex items-center gap-2 bg-white/10 border border-white/20 rounded-full px-4 py-1.5 mb-6 animate-hero-text-1">
<span className="w-2 h-2 bg-green-400 rounded-full animate-pulse" /> <span className="w-2 h-2 bg-green-400 rounded-full animate-pulse" />
<span className="text-white/80 text-sm"> <span className="text-white/80 text-sm">{hero.badge}</span>
Disponible, à l&apos;écoute Benoît vous accompagne de la première pierre à la remise des clés
</span>
</div> </div>
<h1 className="text-4xl md:text-6xl font-bold text-white leading-tight mb-6 animate-hero-text-2"> <h1 className="text-4xl md:text-6xl font-bold text-white leading-tight mb-6">
Maçon &amp; Constructeur<br /> Maçon &amp; Constructeur<br />
<span className="text-orange">dans le Nord</span> <span className="text-orange">dans le Nord</span>
</h1> </h1>
<p className="text-white/70 text-lg md:text-xl max-w-2xl mx-auto mb-8 animate-hero-text-3"> <p className="text-white/70 text-lg md:text-xl max-w-2xl mx-auto mb-8">{hero.subtitle}</p>
Construction de maison, rénovation, assainissement et gros œuvre
expertise autour d&apos;Orchies, Douai et Valenciennes.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center animate-hero-text-3"> <div className="flex flex-col sm:flex-row gap-4 justify-center">
<Link <Link href="/contact" className="inline-flex items-center justify-center gap-2 bg-orange hover:bg-orange-hover text-white font-bold px-8 py-4 rounded-xl text-base transition-colors pulse-glow">
href="/contact" {hero.cta}
className="inline-flex items-center justify-center gap-2 bg-orange hover:bg-orange-hover text-white font-bold px-8 py-4 rounded-xl text-base transition-colors pulse-glow"
>
Demander un devis gratuit
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg> </svg>
</Link> </Link>
<Link <Link href="/realisations" className="inline-flex items-center justify-center gap-2 bg-white/10 hover:bg-white/20 text-white font-semibold px-8 py-4 rounded-xl text-base transition-colors border border-white/20">
href="/realisations" {hero.ctaSecondary}
className="inline-flex items-center justify-center gap-2 bg-white/10 hover:bg-white/20 text-white font-semibold px-8 py-4 rounded-xl text-base transition-colors border border-white/20"
>
Voir nos réalisations
</Link> </Link>
</div> </div>
{/* Stats */}
<div className="mt-14 grid grid-cols-3 gap-6 max-w-lg mx-auto border-t border-white/10 pt-10"> <div className="mt-14 grid grid-cols-3 gap-6 max-w-lg mx-auto border-t border-white/10 pt-10">
{[ {hero.stats.map((s) => (
{ val: "15+", label: "ans d'expérience" },
{ val: "200+", label: "chantiers réalisés" },
{ val: "30km", label: "de rayon d'action" },
].map((s) => (
<div key={s.label} className="text-center"> <div key={s.label} className="text-center">
<div className="text-2xl md:text-3xl font-bold text-orange">{s.val}</div> <div className="text-2xl md:text-3xl font-bold text-orange">{s.val}</div>
<div className="text-white/50 text-xs mt-1">{s.label}</div> <div className="text-white/50 text-xs mt-1">{s.label}</div>
@@ -269,26 +163,10 @@ export default function HomePage() {
</p> </p>
</div> </div>
</ScrollReveal> </ScrollReveal>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{services.map((s, i) => ( {services.map((s, i) => (
<ScrollReveal key={s.title} direction="up" delay={i * 80}> <ScrollReveal key={s.slug} direction="up" delay={i * 80}>
<Link <ServiceCard service={s} />
href={s.href}
className="group block bg-bg-white border border-border rounded-2xl p-6 hover:border-orange hover:shadow-lg transition-all duration-300 card-hover"
>
<div className="text-3xl mb-4">{s.icon}</div>
<h3 className="text-navy font-bold text-lg mb-2 group-hover:text-orange transition-colors">
{s.title}
</h3>
<p className="text-text-light text-sm leading-relaxed">{s.desc}</p>
<div className="mt-4 flex items-center gap-1 text-orange text-sm font-semibold">
En savoir plus
<svg className="w-4 h-4 group-hover:translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</div>
</Link>
</ScrollReveal> </ScrollReveal>
))} ))}
</div> </div>
@@ -304,14 +182,13 @@ export default function HomePage() {
<h2 className="text-3xl md:text-4xl font-bold text-navy mt-2">Pourquoi choisir OBC Maçonnerie ?</h2> <h2 className="text-3xl md:text-4xl font-bold text-navy mt-2">Pourquoi choisir OBC Maçonnerie ?</h2>
</div> </div>
</ScrollReveal> </ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{pilliers.map((p, i) => ( {values.map((v, i) => (
<ScrollReveal key={p.title} direction="up" delay={i * 100}> <ScrollReveal key={v.title} direction="up" delay={i * 100}>
<div className="bg-bg-white rounded-2xl p-6 border border-border text-center h-full"> <div className="bg-bg-white rounded-2xl p-6 border border-border text-center h-full">
<div className="text-4xl mb-4">{p.icon}</div> <div className="text-4xl mb-4">{v.icon}</div>
<h3 className="text-navy font-bold text-lg mb-3">{p.title}</h3> <h3 className="text-navy font-bold text-lg mb-3">{v.title}</h3>
<p className="text-text-light text-sm leading-relaxed">{p.desc}</p> <p className="text-text-light text-sm leading-relaxed">{v.description}</p>
</div> </div>
</ScrollReveal> </ScrollReveal>
))} ))}
@@ -325,17 +202,12 @@ export default function HomePage() {
<ScrollReveal direction="up"> <ScrollReveal direction="up">
<div className="text-center mb-12"> <div className="text-center mb-12">
<span className="text-orange text-sm font-semibold uppercase tracking-widest">Un réseau solide</span> <span className="text-orange text-sm font-semibold uppercase tracking-widest">Un réseau solide</span>
<h2 className="text-3xl md:text-4xl font-bold text-navy mt-2"> <h2 className="text-3xl md:text-4xl font-bold text-navy mt-2">{partnersTitle}</h2>
Seul on va vite, ensemble on va plus loin. <p className="text-text-light mt-4 max-w-xl mx-auto">{partnersMessage}</p>
</h2>
<p className="text-text-light mt-4 max-w-xl mx-auto">
Grâce à notre réseau de partenaires de confiance, nous coordonnons l&apos;ensemble des corps de métier pour que votre maison prenne forme de A à Z.
</p>
</div> </div>
</ScrollReveal> </ScrollReveal>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4"> <div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
{partenaires.map((p, i) => ( {partners.map((p, i) => (
<ScrollReveal key={p.label} direction="up" delay={i * 60}> <ScrollReveal key={p.label} direction="up" delay={i * 60}>
<div className="bg-bg-white border border-border rounded-xl p-4 text-center hover:border-orange hover:shadow-md transition-all"> <div className="bg-bg-white border border-border rounded-xl p-4 text-center hover:border-orange hover:shadow-md transition-all">
<div className="text-2xl mb-2">{p.icon}</div> <div className="text-2xl mb-2">{p.icon}</div>
@@ -344,16 +216,12 @@ export default function HomePage() {
</ScrollReveal> </ScrollReveal>
))} ))}
</div> </div>
<ScrollReveal direction="up" delay={200}> <ScrollReveal direction="up" delay={200}>
<div className="mt-10 bg-navy rounded-2xl p-6 md:p-8 text-center"> <div className="mt-10 bg-navy rounded-2xl p-6 md:p-8 text-center">
<p className="text-white text-base md:text-lg font-medium"> <p className="text-white text-base md:text-lg font-medium">
Un seul interlocuteur pour coordonner l&apos;ensemble de votre projet de la démolition à la remise des clés. Un seul interlocuteur pour coordonner l&apos;ensemble de votre projet de la démolition à la remise des clés.
</p> </p>
<Link <Link href="/partenaires" className="inline-flex items-center gap-2 mt-4 text-orange-light hover:text-white font-semibold transition-colors">
href="/partenaires"
className="inline-flex items-center gap-2 mt-4 text-orange-light hover:text-white font-semibold transition-colors"
>
Découvrir notre réseau Découvrir notre réseau
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
@@ -369,33 +237,25 @@ export default function HomePage() {
<div className="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 text-center"> <div className="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<ScrollReveal direction="up"> <ScrollReveal direction="up">
<span className="text-orange text-sm font-semibold uppercase tracking-widest">Secteur d&apos;activité</span> <span className="text-orange text-sm font-semibold uppercase tracking-widest">Secteur d&apos;activité</span>
<h2 className="text-3xl md:text-4xl font-bold text-navy mt-2 mb-4"> <h2 className="text-3xl md:text-4xl font-bold text-navy mt-2 mb-4">Nous intervenons dans toute la région</h2>
Nous intervenons dans toute la région
</h2>
<p className="text-text-light max-w-xl mx-auto mb-10"> <p className="text-text-light max-w-xl mx-auto mb-10">
OBC Maçonnerie intervient dans un rayon de 20 à 30 km autour de Mouchin (Nord 59). OBC Maçonnerie intervient dans un rayon de {zoneDescription}.
</p> </p>
</ScrollReveal> </ScrollReveal>
<div className="flex flex-wrap justify-center gap-3 mb-8"> <div className="flex flex-wrap justify-center gap-3 mb-8">
{villes.map((v, i) => ( {zones.map((v, i) => (
<ScrollReveal key={v} direction="up" delay={i * 50}> <ScrollReveal key={v} direction="up" delay={i * 50}>
<span className="inline-flex items-center gap-1.5 bg-bg-white border border-border text-navy font-medium text-sm px-4 py-2 rounded-full hover:border-orange hover:shadow-sm transition-all"> <span className="inline-flex items-center gap-1.5 bg-bg-white border border-border text-navy font-medium text-sm px-4 py-2 rounded-full hover:border-orange hover:shadow-sm transition-all">
<span className="text-orange">📍</span> <span className="text-orange">📍</span>{v}
{v}
</span> </span>
</ScrollReveal> </ScrollReveal>
))} ))}
</div> </div>
<ScrollReveal direction="up" delay={100}> <ScrollReveal direction="up" delay={100}>
<p className="text-text-light text-sm italic"> <p className="text-text-light text-sm italic">
Et dans toutes les communes à 20-30 km autour de Mouchin contactez-nous pour vérifier votre zone. Et dans toutes les communes à {zoneDescription} contactez-nous pour vérifier votre zone.
</p> </p>
<Link <Link href="/contact" className="inline-flex items-center gap-2 mt-6 bg-orange hover:bg-orange-hover text-white font-bold px-7 py-3.5 rounded-xl transition-colors">
href="/contact"
className="inline-flex items-center gap-2 mt-6 bg-orange hover:bg-orange-hover text-white font-bold px-7 py-3.5 rounded-xl transition-colors"
>
Demander un devis dans ma commune Demander un devis dans ma commune
</Link> </Link>
</ScrollReveal> </ScrollReveal>
@@ -411,34 +271,28 @@ export default function HomePage() {
<h2 className="text-3xl md:text-4xl font-bold text-navy mt-2">Aperçu de nos réalisations</h2> <h2 className="text-3xl md:text-4xl font-bold text-navy mt-2">Aperçu de nos réalisations</h2>
</div> </div>
</ScrollReveal> </ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6"> <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{realisations.map((r, i) => ( {realisations.map((r, i) => {
<ScrollReveal key={r.title} direction="up" delay={i * 100}> const colors = ["bg-navy", "bg-stone", "bg-orange"];
<div className="bg-bg-white rounded-2xl overflow-hidden border border-border hover:shadow-lg transition-all group card-hover"> return (
<div className={`${r.color} h-44 flex items-center justify-center`}> <ScrollReveal key={r.title} direction="up" delay={i * 100}>
<span className="text-white/20 text-8xl font-bold">{i + 1}</span> <div className="bg-bg-white rounded-2xl overflow-hidden border border-border hover:shadow-lg transition-all group card-hover">
<div className={`${colors[i % colors.length]} h-44 flex items-center justify-center`}>
<span className="text-white/20 text-8xl font-bold">{i + 1}</span>
</div>
<div className="p-5">
<span className="inline-block bg-bg-muted text-text-light text-xs font-semibold px-2 py-1 rounded-full mb-2">{r.ville}</span>
<h3 className="text-navy font-bold text-base mb-1 group-hover:text-orange transition-colors">{r.title}</h3>
<p className="text-text-light text-sm">{r.description}</p>
</div>
</div> </div>
<div className="p-5"> </ScrollReveal>
<span className="inline-block bg-bg-muted text-text-light text-xs font-semibold px-2 py-1 rounded-full mb-2"> );
{r.cat} })}
</span>
<h3 className="text-navy font-bold text-base mb-1 group-hover:text-orange transition-colors">
{r.title}
</h3>
<p className="text-text-light text-sm">{r.desc}</p>
</div>
</div>
</ScrollReveal>
))}
</div> </div>
<ScrollReveal direction="up" delay={150}> <ScrollReveal direction="up" delay={150}>
<div className="text-center mt-8"> <div className="text-center mt-8">
<Link <Link href="/realisations" className="inline-flex items-center gap-2 border-2 border-navy text-navy hover:bg-navy hover:text-white font-bold px-7 py-3.5 rounded-xl transition-colors">
href="/realisations"
className="inline-flex items-center gap-2 border-2 border-navy text-navy hover:bg-navy hover:text-white font-bold px-7 py-3.5 rounded-xl transition-colors"
>
Voir toutes nos réalisations Voir toutes nos réalisations
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
@@ -458,20 +312,10 @@ export default function HomePage() {
<h2 className="text-3xl md:text-4xl font-bold text-white mt-2">Témoignages clients</h2> <h2 className="text-3xl md:text-4xl font-bold text-white mt-2">Témoignages clients</h2>
</div> </div>
</ScrollReveal> </ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6"> <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{temoignages.map((t, i) => ( {testimonials.map((t, i) => (
<ScrollReveal key={t.nom} direction="up" delay={i * 100}> <ScrollReveal key={t.name} direction="up" delay={i * 100}>
<div className="bg-white/5 border border-white/10 rounded-2xl p-6 h-full flex flex-col"> <TestimonialCard t={t} />
<StarRating note={t.note} />
<p className="text-white/80 text-sm leading-relaxed mt-4 flex-1 italic">
&ldquo;{t.texte}&rdquo;
</p>
<div className="mt-5 pt-4 border-t border-white/10">
<p className="text-white font-semibold text-sm">{t.nom}</p>
<p className="text-white/40 text-xs">{t.lieu} {t.projet}</p>
</div>
</div>
</ScrollReveal> </ScrollReveal>
))} ))}
</div> </div>
@@ -487,26 +331,10 @@ export default function HomePage() {
<h2 className="text-3xl md:text-4xl font-bold text-navy mt-2">FAQ</h2> <h2 className="text-3xl md:text-4xl font-bold text-navy mt-2">FAQ</h2>
</div> </div>
</ScrollReveal> </ScrollReveal>
<div className="space-y-4"> <div className="space-y-4">
{faqs.map((f, i) => ( {faqItems.map((f, i) => (
<ScrollReveal key={f.q} direction="up" delay={i * 60}> <ScrollReveal key={f.question} direction="up" delay={i * 60}>
<details className="group bg-bg-white border border-border rounded-2xl overflow-hidden"> <FAQAccordion item={f} />
<summary className="flex items-center justify-between px-6 py-4 cursor-pointer font-semibold text-navy hover:text-orange transition-colors list-none">
{f.q}
<svg
className="w-5 h-5 text-text-muted group-open:rotate-180 transition-transform shrink-0 ml-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</summary>
<div className="px-6 pb-5 text-text-light text-sm leading-relaxed border-t border-border-light pt-4">
{f.a}
</div>
</details>
</ScrollReveal> </ScrollReveal>
))} ))}
</div> </div>
@@ -522,9 +350,7 @@ export default function HomePage() {
<h2 className="text-3xl md:text-4xl font-bold text-navy mt-2">Parlez-nous de votre projet</h2> <h2 className="text-3xl md:text-4xl font-bold text-navy mt-2">Parlez-nous de votre projet</h2>
<p className="text-text-light mt-3"> <p className="text-text-light mt-3">
Réponse sous 24h ou appelez directement Benoît au{" "} Réponse sous 24h ou appelez directement Benoît au{" "}
<a href="tel:0674453089" className="text-orange font-bold hover:underline"> <a href={`tel:${phoneRaw}`} className="text-orange font-bold hover:underline">{phone}</a>
06 74 45 30 89
</a>
</p> </p>
</div> </div>
</ScrollReveal> </ScrollReveal>

View File

@@ -3,58 +3,21 @@ import Link from "next/link";
import Navbar from "@/components/marketing/Navbar"; import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer"; import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal"; import ScrollReveal from "@/components/animations/ScrollReveal";
import { getPartners, getSiteConfig } from "@/lib/content";
export const metadata: Metadata = { export async function generateMetadata(): Promise<Metadata> {
title: "Notre Réseau de Partenaires | OBC Maçonnerie Nord", const config = await getSiteConfig();
description: return {
"OBC Maçonnerie coordonne un réseau d'artisans partenaires de confiance pour livrer votre maison de A à Z : électricité, plomberie, charpente, isolation, menuiserie, carrelage, peinture.", title: "Notre Réseau de Partenaires | OBC Maçonnerie Nord",
alternates: { canonical: "https://obc-maconnerie.fr/partenaires" }, description:
}; "OBC Maçonnerie coordonne un réseau d'artisans partenaires de confiance pour livrer votre maison de A à Z : électricité, plomberie, charpente, isolation, menuiserie, carrelage, peinture.",
alternates: { canonical: `${config.url}/partenaires` },
};
}
const partenaires = [ export default async function PartenairesPage() {
{ const partenaires = await getPartners();
icon: "⚡",
metier: "Électricité",
desc: "Installation électrique aux normes NF C 15-100, tableau de distribution, prises, éclairage.",
},
{
icon: "🔧",
metier: "Plomberie",
desc: "Plomberie sanitaire, chauffage central, installation de salles de bains et cuisines.",
},
{
icon: "🪵",
metier: "Charpente",
desc: "Charpente traditionnelle ou industrielle, structure bois pour combles aménageables ou non.",
},
{
icon: "🏚️",
metier: "Couverture",
desc: "Pose de toiture, tuiles, ardoises, zinc — étanchéité et finitions soignées.",
},
{
icon: "🧱",
metier: "Isolation",
desc: "Isolation thermique et phonique par l'intérieur ou l'extérieur, combles, planchers.",
},
{
icon: "🚪",
metier: "Menuiserie",
desc: "Fenêtres, portes, vérandas, volets — menuiserie bois, PVC ou aluminium.",
},
{
icon: "🔳",
metier: "Carrelage & Revêtements",
desc: "Pose de carrelage, parquet, faïence — pour sols et murs, intérieur et extérieur.",
},
{
icon: "🎨",
metier: "Peinture",
desc: "Peinture intérieure et extérieure, enduits décoratifs, ravalement de façade.",
},
];
export default function PartenairesPage() {
return ( return (
<main id="main-content" className="min-h-screen"> <main id="main-content" className="min-h-screen">
<Navbar /> <Navbar />
@@ -88,10 +51,10 @@ export default function PartenairesPage() {
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-5"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-5">
{partenaires.map((p, i) => ( {partenaires.map((p, i) => (
<ScrollReveal key={p.metier} direction="up" delay={i * 70}> <ScrollReveal key={p.label} direction="up" delay={i * 70}>
<div className="bg-bg-white border border-border rounded-2xl p-5 text-center h-full hover:border-orange hover:shadow-md transition-all"> <div className="bg-bg-white border border-border rounded-2xl p-5 text-center h-full hover:border-orange hover:shadow-md transition-all">
<div className="text-4xl mb-3">{p.icon}</div> <div className="text-4xl mb-3">{p.icon}</div>
<h3 className="text-navy font-bold text-base mb-2">{p.metier}</h3> <h3 className="text-navy font-bold text-base mb-2">{p.label}</h3>
<p className="text-text-light text-xs leading-relaxed">{p.desc}</p> <p className="text-text-light text-xs leading-relaxed">{p.desc}</p>
</div> </div>
</ScrollReveal> </ScrollReveal>

View File

@@ -3,62 +3,24 @@ import Link from "next/link";
import Navbar from "@/components/marketing/Navbar"; import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer"; import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal"; import ScrollReveal from "@/components/animations/ScrollReveal";
import { getRealisations, getSiteConfig } from "@/lib/content";
export const metadata: Metadata = { export async function generateMetadata(): Promise<Metadata> {
title: "Nos Réalisations | Chantiers OBC Maçonnerie Nord", const config = await getSiteConfig();
description: return {
"Découvrez les réalisations d'OBC Maçonnerie : constructions de maisons, rénovations, assainissement et créations d'accès dans le Nord (59). Galerie photos.", title: "Nos Réalisations | Chantiers OBC Maçonnerie Nord",
alternates: { canonical: "https://obc-maconnerie.fr/realisations" }, description:
}; "Découvrez les réalisations d'OBC Maçonnerie : constructions de maisons, rénovations, assainissement et créations d'accès dans le Nord (59). Galerie photos.",
alternates: { canonical: `${config.url}/realisations` },
const realisations = [ };
{ }
categorie: "Construction neuve",
titre: "Maison individuelle à Orchies",
desc: "Construction d'une maison de 130 m² — fondations, gros œuvre, dalle béton et ossature.",
zone: "Orchies (59)",
color: "bg-navy",
},
{
categorie: "Rénovation",
titre: "Rénovation complète à Douai",
desc: "Restructuration intérieure complète d'une maison de ville : abattage de cloisons, création d'un escalier neuf, doublages.",
zone: "Douai (59)",
color: "bg-stone",
},
{
categorie: "Assainissement",
titre: "Mise aux normes à Saint-Amand",
desc: "Remplacement d'une fosse septique vétuste par une micro-station d'épuration conforme aux normes.",
zone: "Saint-Amand-les-Eaux (59)",
color: "bg-navy-light",
},
{
categorie: "Création d'accès",
titre: "Entrée en béton imprimé à Mérignies",
desc: "Création d'une entrée de propriété en béton imprimé effet pavés, avec caniveau de drainage.",
zone: "Mérignies (59)",
color: "bg-orange",
},
{
categorie: "Construction neuve",
titre: "Extension ossature bois à Flines",
desc: "Agrandissement d'une maison existante par extension ossature bois, fondations et dalle.",
zone: "Flines-lès-Raches (59)",
color: "bg-navy",
},
{
categorie: "Démolition",
titre: "Démolition & reconstruction à Valenciennes",
desc: "Démolition d'un bâtiment annexe et curage d'une grange pour préparer une rénovation complète.",
zone: "Valenciennes (59)",
color: "bg-stone",
},
];
const cats = ["Tous", "Construction neuve", "Rénovation", "Assainissement", "Création d'accès", "Démolition"]; const cats = ["Tous", "Construction neuve", "Rénovation", "Assainissement", "Création d'accès", "Démolition"];
export default function RealisationsPage() { export default async function RealisationsPage() {
const [realisations, config] = await Promise.all([getRealisations(), getSiteConfig()]);
const { phone, phoneRaw } = config;
return ( return (
<main id="main-content" className="min-h-screen"> <main id="main-content" className="min-h-screen">
<Navbar /> <Navbar />
@@ -100,7 +62,7 @@ export default function RealisationsPage() {
<div className="max-w-6xl mx-auto px-4 sm:px-6"> <div className="max-w-6xl mx-auto px-4 sm:px-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{realisations.map((r, i) => ( {realisations.map((r, i) => (
<ScrollReveal key={r.titre} direction="up" delay={i * 80}> <ScrollReveal key={r.title} direction="up" delay={i * 80}>
<div className="bg-bg-white border border-border rounded-2xl overflow-hidden hover:shadow-lg transition-all group card-hover"> <div className="bg-bg-white border border-border rounded-2xl overflow-hidden hover:shadow-lg transition-all group card-hover">
<div className={`${r.color} h-48 flex items-center justify-center relative`}> <div className={`${r.color} h-48 flex items-center justify-center relative`}>
<span className="text-white/10 text-8xl font-black">{i + 1}</span> <span className="text-white/10 text-8xl font-black">{i + 1}</span>
@@ -112,12 +74,12 @@ export default function RealisationsPage() {
</div> </div>
<div className="p-5"> <div className="p-5">
<h3 className="text-navy font-bold text-base mb-2 group-hover:text-orange transition-colors"> <h3 className="text-navy font-bold text-base mb-2 group-hover:text-orange transition-colors">
{r.titre} {r.title}
</h3> </h3>
<p className="text-text-light text-sm leading-relaxed mb-3">{r.desc}</p> <p className="text-text-light text-sm leading-relaxed mb-3">{r.description}</p>
<div className="flex items-center gap-1 text-text-muted text-xs"> <div className="flex items-center gap-1 text-text-muted text-xs">
<span>📍</span> <span>📍</span>
<span>{r.zone}</span> <span>{r.ville}</span>
</div> </div>
</div> </div>
</div> </div>
@@ -141,10 +103,10 @@ export default function RealisationsPage() {
Demander un devis gratuit Demander un devis gratuit
</Link> </Link>
<a <a
href="tel:0674453089" href={`tel:${phoneRaw}`}
className="inline-flex items-center justify-center gap-2 border-2 border-navy text-navy hover:bg-navy hover:text-white font-bold px-7 py-3.5 rounded-xl transition-colors" className="inline-flex items-center justify-center gap-2 border-2 border-navy text-navy hover:bg-navy hover:text-white font-bold px-7 py-3.5 rounded-xl transition-colors"
> >
06 74 45 30 89 {phone}
</a> </a>
</div> </div>
</div> </div>

View File

@@ -6,7 +6,7 @@ export const metadata: Metadata = {
description: description:
"Rénovation de maison et appartement à Douai. OBC Maçonnerie, maçon expert en rénovation dans le Nord (59). Devis gratuit.", "Rénovation de maison et appartement à Douai. OBC Maçonnerie, maçon expert en rénovation dans le Nord (59). Devis gratuit.",
keywords: ["rénovation maison Douai", "maçon rénovation Douai", "rénovation appartement Douai", "travaux rénovation Douai"], keywords: ["rénovation maison Douai", "maçon rénovation Douai", "rénovation appartement Douai", "travaux rénovation Douai"],
alternates: { canonical: "https://obc-maconnerie.fr/renovation-maison-douai" }, alternates: { canonical: "https://obc-terrassement.fr/renovation-maison-douai" },
}; };
export default function RenovationMaisonDouaiPage() { export default function RenovationMaisonDouaiPage() {

View File

@@ -6,7 +6,7 @@ export const metadata: Metadata = {
description: description:
"Rénovation de maison et appartement à Orchies. OBC Maçonnerie, maçon expert en rénovation dans le Nord (59). Devis gratuit.", "Rénovation de maison et appartement à Orchies. OBC Maçonnerie, maçon expert en rénovation dans le Nord (59). Devis gratuit.",
keywords: ["rénovation maison Orchies", "maçon rénovation Orchies", "rénovation appartement Orchies"], keywords: ["rénovation maison Orchies", "maçon rénovation Orchies", "rénovation appartement Orchies"],
alternates: { canonical: "https://obc-maconnerie.fr/renovation-maison-orchies" }, alternates: { canonical: "https://obc-terrassement.fr/renovation-maison-orchies" },
}; };
export default function RenovationMaisonOrchiesPage() { export default function RenovationMaisonOrchiesPage() {

View File

@@ -4,8 +4,12 @@ import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer"; import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal"; import ScrollReveal from "@/components/animations/ScrollReveal";
import ContactForm from "@/components/marketing/ContactForm"; import ContactForm from "@/components/marketing/ContactForm";
import { getSiteConfig } from "@/lib/content";
export async function generateMetadata(): Promise<Metadata> {
const config = await getSiteConfig();
return {
export const metadata: Metadata = {
title: "Rénovation Maison & Appartement Nord 59 | OBC Maçonnerie", title: "Rénovation Maison & Appartement Nord 59 | OBC Maçonnerie",
description: description:
"Rénovation complète ou partielle de maison et appartement dans le Nord. Benoît Colin vous conseille et adapte chaque projet. Devis gratuit.", "Rénovation complète ou partielle de maison et appartement dans le Nord. Benoît Colin vous conseille et adapte chaque projet. Devis gratuit.",
@@ -17,8 +21,9 @@ export const metadata: Metadata = {
"rénovation maison Orchies", "rénovation maison Orchies",
"travaux rénovation Nord", "travaux rénovation Nord",
], ],
alternates: { canonical: "https://obc-maconnerie.fr/renovation" }, alternates: { canonical: `${config.url}/renovation` },
}; };
}
const typesTravaux = [ const typesTravaux = [
{ icon: "🏚️", title: "Rénovation complète", desc: "Restructuration totale d'une maison ancienne, de la démolition des cloisons existantes à la pose des revêtements." }, { icon: "🏚️", title: "Rénovation complète", desc: "Restructuration totale d'une maison ancienne, de la démolition des cloisons existantes à la pose des revêtements." },
@@ -29,7 +34,9 @@ const typesTravaux = [
{ icon: "🏠", title: "Rénovation appartement", desc: "Transformation d'appartements : redistribution des pièces, mise aux normes, travaux de second œuvre." }, { icon: "🏠", title: "Rénovation appartement", desc: "Transformation d'appartements : redistribution des pièces, mise aux normes, travaux de second œuvre." },
]; ];
export default function RenovationPage() { export default async function RenovationPage() {
const config = await getSiteConfig();
const { phone, phoneRaw } = config;
return ( return (
<main id="main-content" className="min-h-screen"> <main id="main-content" className="min-h-screen">
<Navbar /> <Navbar />
@@ -55,9 +62,7 @@ export default function RenovationPage() {
<Link href="/contact" className="inline-flex items-center justify-center gap-2 bg-orange hover:bg-orange-hover text-white font-bold px-7 py-3.5 rounded-xl transition-colors pulse-glow"> <Link href="/contact" className="inline-flex items-center justify-center gap-2 bg-orange hover:bg-orange-hover text-white font-bold px-7 py-3.5 rounded-xl transition-colors pulse-glow">
Demander un devis gratuit Demander un devis gratuit
</Link> </Link>
<a href="tel:0674453089" className="inline-flex items-center justify-center gap-2 bg-white/10 hover:bg-white/20 text-white font-semibold px-7 py-3.5 rounded-xl transition-colors border border-white/20"> <a href={`tel:${phoneRaw}`} className="inline-flex items-center justify-center gap-2 bg-white/10 hover:bg-white/20 text-white font-semibold px-7 py-3.5 rounded-xl transition-colors border border-white/20">{phone}</a>
06 74 45 30 89
</a>
</div> </div>
</ScrollReveal> </ScrollReveal>
</div> </div>

View File

@@ -1,6 +1,7 @@
import type { MetadataRoute } from "next"; import type { MetadataRoute } from "next";
import { siteConfig } from "@/lib/site-config";
const BASE_URL = process.env.NEXT_PUBLIC_APP_URL || "https://obc-maconnerie.fr"; const BASE_URL = process.env.NEXT_PUBLIC_APP_URL || siteConfig.url;
export default function robots(): MetadataRoute.Robots { export default function robots(): MetadataRoute.Robots {
return { return {

View File

@@ -3,65 +3,23 @@ import Link from "next/link";
import Navbar from "@/components/marketing/Navbar"; import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer"; import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal"; import ScrollReveal from "@/components/animations/ScrollReveal";
import { getServices, getSiteConfig } from "@/lib/content";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Nos Services | Construction, Rénovation, Assainissement", title: "Nos Services | Construction, Rénovation, Assainissement",
description: description:
"Tous les services d'OBC Maçonnerie : construction de maison, rénovation, assainissement, création d'accès et démolition dans le Nord (59). Devis gratuit.", "Tous les services d'OBC Maçonnerie : construction de maison, rénovation, assainissement, création d'accès et démolition dans le Nord (59). Devis gratuit.",
alternates: { canonical: "https://obc-maconnerie.fr/services" }, alternates: { canonical: "https://obc-terrassement.fr/services" },
}; };
const services = [ export default async function ServicesPage() {
{ const [services, config] = await Promise.all([getServices(), getSiteConfig()]);
icon: "🏠", const { phone, phoneRaw } = config;
title: "Construction de maison",
desc: "De la conception au gros œuvre, Benoît Colin vous accompagne dans la construction de votre maison individuelle. Fondations, ossature bois, dalles, murs porteurs — tout est pris en charge avec rigueur et savoir-faire.",
href: "/construction-maison",
points: ["Maison individuelle", "Ossature bois", "Fondations", "Gros œuvre complet"],
},
{
icon: "🔨",
title: "Rénovation",
desc: "Que ce soit une rénovation partielle ou complète, OBC Maçonnerie s'adapte à votre projet. Maison ancienne, appartement, restructuration intérieure — chaque chantier est unique et traité comme tel.",
href: "/renovation",
points: ["Rénovation complète", "Restructuration intérieure", "Maison de ville", "Extension"],
},
{
icon: "💧",
title: "Assainissement",
desc: "Mise aux normes de votre système d'assainissement, création d'un nouveau dispositif ou réhabilitation de l'existant. OBC Maçonnerie réalise vos travaux d'assainissement dans les règles de l'art.",
href: "/assainissement",
points: ["Assainissement individuel", "Mise aux normes", "Fosse septique", "Épandage"],
},
{
icon: "🚧",
title: "Création d'accès",
desc: "Voiries, entrées de propriété, chemins, allées — OBC Maçonnerie crée vos accès selon vos besoins et vos envies. Béton, béton imprimé, pavés ou grave compactée.",
href: "/creation-acces",
points: ["Voiries privées", "Entrées de propriété", "Chemins ruraux", "Béton imprimé"],
},
{
icon: "🏗️",
title: "Démolition",
desc: "Démolition totale ou partielle de bâtiment, destruction de murs porteurs, dépose de chapes — OBC Maçonnerie intervient avec tout le matériel et les garanties de sécurité nécessaires.",
href: "/demolition",
points: ["Démolition totale", "Démolition partielle", "Murs porteurs", "Évacuation des gravats"],
},
{
icon: "🤝",
title: "Conseil & Accompagnement",
desc: "Benoît vous guide à chaque étape de votre projet : choix des matériaux, adaptation de plans, coordination des artisans partenaires. Un seul interlocuteur pour un projet serein.",
href: "/contact",
points: ["Conseils matériaux", "Adaptation de plans", "Coordination artisans", "Suivi de chantier"],
},
];
export default function ServicesPage() {
return ( return (
<main id="main-content" className="min-h-screen"> <main id="main-content" className="min-h-screen">
<Navbar /> <Navbar />
{/* Hero */}
<section className="bg-navy py-16 md:py-20"> <section className="bg-navy py-16 md:py-20">
<div className="max-w-4xl mx-auto px-4 sm:px-6 text-center"> <div className="max-w-4xl mx-auto px-4 sm:px-6 text-center">
<ScrollReveal direction="up"> <ScrollReveal direction="up">
@@ -74,61 +32,54 @@ export default function ServicesPage() {
</div> </div>
</section> </section>
{/* Services */}
<section className="py-16 md:py-20 bg-bg"> <section className="py-16 md:py-20 bg-bg">
<div className="max-w-5xl mx-auto px-4 sm:px-6 space-y-8"> <div className="max-w-5xl mx-auto px-4 sm:px-6 space-y-8">
{services.map((s, i) => ( {services.map((s, i) => {
<ScrollReveal key={s.title} direction="up" delay={i * 60}> const href = s.slug === "conseil" ? "/contact" : `/${s.slug}`;
<div className="bg-bg-white border border-border rounded-2xl p-6 md:p-8 flex flex-col md:flex-row gap-6"> return (
<div className="text-5xl shrink-0">{s.icon}</div> <ScrollReveal key={s.slug} direction="up" delay={i * 60}>
<div className="flex-1"> <div className="bg-bg-white border border-border rounded-2xl p-6 md:p-8 flex flex-col md:flex-row gap-6">
<h2 className="text-xl font-bold text-navy mb-2">{s.title}</h2> <div className="text-5xl shrink-0">{s.icon}</div>
<p className="text-text-light text-sm leading-relaxed mb-4">{s.desc}</p> <div className="flex-1">
<div className="flex flex-wrap gap-2 mb-5"> <h2 className="text-xl font-bold text-navy mb-2">{s.title}</h2>
{s.points.map((p) => ( <p className="text-text-light text-sm leading-relaxed mb-4">{s.longDescription}</p>
<span key={p} className="bg-bg-muted text-text-light text-xs font-medium px-3 py-1 rounded-full"> <div className="flex flex-wrap gap-2 mb-5">
{p} {s.keywords.map((k) => (
</span> <span key={k} className="bg-bg-muted text-text-light text-xs font-medium px-3 py-1 rounded-full">
))} {k}
</span>
))}
</div>
<Link
href={href}
className="inline-flex items-center gap-1.5 text-orange font-semibold text-sm hover:underline"
>
En savoir plus
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</Link>
</div> </div>
<Link
href={s.href}
className="inline-flex items-center gap-1.5 text-orange font-semibold text-sm hover:underline"
>
En savoir plus
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</Link>
</div> </div>
</div> </ScrollReveal>
</ScrollReveal> );
))} })}
</div> </div>
</section> </section>
{/* CTA */}
<section className="py-16 bg-stone-bg"> <section className="py-16 bg-stone-bg">
<div className="max-w-2xl mx-auto px-4 text-center"> <div className="max-w-2xl mx-auto px-4 text-center">
<ScrollReveal direction="up"> <ScrollReveal direction="up">
<h2 className="text-2xl md:text-3xl font-bold text-navy mb-4"> <h2 className="text-2xl md:text-3xl font-bold text-navy mb-4">Vous avez un projet ? Parlons-en.</h2>
Vous avez un projet ? Parlons-en.
</h2>
<p className="text-text-light mb-6"> <p className="text-text-light mb-6">
Benoît se déplace gratuitement pour évaluer votre projet et vous remettre un devis détaillé. Benoît se déplace gratuitement pour évaluer votre projet et vous remettre un devis détaillé.
</p> </p>
<div className="flex flex-col sm:flex-row gap-4 justify-center"> <div className="flex flex-col sm:flex-row gap-4 justify-center">
<Link <Link href="/contact" className="inline-flex items-center justify-center gap-2 bg-orange hover:bg-orange-hover text-white font-bold px-7 py-3.5 rounded-xl transition-colors">
href="/contact"
className="inline-flex items-center justify-center gap-2 bg-orange hover:bg-orange-hover text-white font-bold px-7 py-3.5 rounded-xl transition-colors"
>
Demander un devis gratuit Demander un devis gratuit
</Link> </Link>
<a <a href={`tel:${phoneRaw}`} className="inline-flex items-center justify-center gap-2 border-2 border-navy text-navy hover:bg-navy hover:text-white font-bold px-7 py-3.5 rounded-xl transition-colors">
href="tel:0674453089" {phone}
className="inline-flex items-center justify-center gap-2 border-2 border-navy text-navy hover:bg-navy hover:text-white font-bold px-7 py-3.5 rounded-xl transition-colors"
>
06 74 45 30 89
</a> </a>
</div> </div>
</ScrollReveal> </ScrollReveal>

View File

@@ -1,6 +1,7 @@
import type { MetadataRoute } from "next"; import type { MetadataRoute } from "next";
import { siteConfig } from "@/lib/site-config";
const BASE_URL = process.env.NEXT_PUBLIC_APP_URL || "https://obc-maconnerie.fr"; const BASE_URL = process.env.NEXT_PUBLIC_APP_URL || siteConfig.url;
export default function sitemap(): MetadataRoute.Sitemap { export default function sitemap(): MetadataRoute.Sitemap {
const now = new Date(); const now = new Date();

View File

@@ -1,6 +1,19 @@
import Link from "next/link"; import Link from "next/link";
import { siteConfig } from "@/lib/site-config";
export default function Footer() { export default function Footer() {
const {
name,
dirigeant,
phone,
phoneRaw,
address,
siren,
footerServicesNav,
footerMainNav,
footerLegalNav,
} = siteConfig;
return ( return (
<footer className="bg-navy text-white pt-12 pb-6"> <footer className="bg-navy text-white pt-12 pb-6">
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
@@ -17,35 +30,33 @@ export default function Footer() {
</div> </div>
</div> </div>
<p className="text-white/70 text-sm leading-relaxed mb-4 max-w-xs"> <p className="text-white/70 text-sm leading-relaxed mb-4 max-w-xs">
Benoît Colin, maçon expert en construction de maison, rénovation et gros œuvre dans le Nord. De la première pierre à la remise des clés. {dirigeant}, maçon expert en construction de maison, rénovation et gros
œuvre dans le Nord. De la première pierre à la remise des clés.
</p> </p>
<a <a
href="tel:0674453089" href={`tel:${phoneRaw}`}
className="inline-flex items-center gap-2 text-orange-light font-bold text-base hover:text-white transition-colors" className="inline-flex items-center gap-2 text-orange-light font-bold text-base hover:text-white transition-colors"
> >
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg> </svg>
06 74 45 30 89 {phone}
</a> </a>
<p className="text-white/40 text-xs mt-2"> <p className="text-white/40 text-xs mt-2">{address}</p>
221 Route de Saint-Amand, 59310 Mouchin
</p>
</div> </div>
{/* Services */} {/* Services */}
<div> <div>
<h4 className="text-white font-semibold text-sm mb-4 uppercase tracking-wide">Services</h4> <h4 className="text-white font-semibold text-sm mb-4 uppercase tracking-wide">
Services
</h4>
<ul className="space-y-2"> <ul className="space-y-2">
{[ {footerServicesNav.map((item) => (
{ href: "/construction-maison", label: "Construction de maison" },
{ href: "/renovation", label: "Rénovation" },
{ href: "/assainissement", label: "Assainissement" },
{ href: "/creation-acces", label: "Création d'accès" },
{ href: "/demolition", label: "Démolition" },
].map((item) => (
<li key={item.href}> <li key={item.href}>
<Link href={item.href} className="text-white/60 hover:text-white text-sm transition-colors"> <Link
href={item.href}
className="text-white/60 hover:text-white text-sm transition-colors"
>
{item.label} {item.label}
</Link> </Link>
</li> </li>
@@ -53,33 +64,33 @@ export default function Footer() {
</ul> </ul>
</div> </div>
{/* Navigation */} {/* Navigation + Légal */}
<div> <div>
<h4 className="text-white font-semibold text-sm mb-4 uppercase tracking-wide">Navigation</h4> <h4 className="text-white font-semibold text-sm mb-4 uppercase tracking-wide">
Navigation
</h4>
<ul className="space-y-2"> <ul className="space-y-2">
{[ {footerMainNav.map((item) => (
{ href: "/", label: "Accueil" },
{ href: "/realisations", label: "Réalisations" },
{ href: "/partenaires", label: "Partenaires" },
{ href: "/contact", label: "Contact" },
{ href: "/blog", label: "Blog" },
].map((item) => (
<li key={item.href}> <li key={item.href}>
<Link href={item.href} className="text-white/60 hover:text-white text-sm transition-colors"> <Link
href={item.href}
className="text-white/60 hover:text-white text-sm transition-colors"
>
{item.label} {item.label}
</Link> </Link>
</li> </li>
))} ))}
</ul> </ul>
<h4 className="text-white font-semibold text-sm mb-3 mt-5 uppercase tracking-wide">Légal</h4> <h4 className="text-white font-semibold text-sm mb-3 mt-5 uppercase tracking-wide">
Légal
</h4>
<ul className="space-y-2"> <ul className="space-y-2">
{[ {footerLegalNav.map((item) => (
{ href: "/mentions-legales", label: "Mentions légales" },
{ href: "/confidentialite", label: "Confidentialité" },
{ href: "/cgv", label: "CGV" },
].map((item) => (
<li key={item.href}> <li key={item.href}>
<Link href={item.href} className="text-white/60 hover:text-white text-sm transition-colors"> <Link
href={item.href}
className="text-white/60 hover:text-white text-sm transition-colors"
>
{item.label} {item.label}
</Link> </Link>
</li> </li>
@@ -91,10 +102,12 @@ export default function Footer() {
{/* Bottom */} {/* Bottom */}
<div className="pt-6 flex flex-col md:flex-row items-center justify-between gap-3"> <div className="pt-6 flex flex-col md:flex-row items-center justify-between gap-3">
<p className="text-white/40 text-xs text-center md:text-left"> <p className="text-white/40 text-xs text-center md:text-left">
&copy; {new Date().getFullYear()} OBC Maçonnerie &mdash; Benoît Colin &middot; SIREN 531 827 871 &copy; {new Date().getFullYear()} {name} &mdash; {dirigeant} &middot; SIREN{" "}
{siren.replace(/(\d{3})(\d{3})(\d{3})/, "$1 $2 $3")}
</p> </p>
<p className="text-white/40 text-xs text-center md:text-right"> <p className="text-white/40 text-xs text-center md:text-right">
Orchies &middot; Mouchin &middot; Douai &middot; Valenciennes &middot; Saint-Amand-les-Eaux &mdash;{" "} Orchies &middot; Mouchin &middot; Douai &middot; Valenciennes &middot;
Saint-Amand-les-Eaux &mdash;{" "}
<span className="text-white/30">Site réalisé par HookLab</span> <span className="text-white/30">Site réalisé par HookLab</span>
</p> </p>
</div> </div>

View File

@@ -3,6 +3,7 @@ import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer"; import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal"; import ScrollReveal from "@/components/animations/ScrollReveal";
import ContactForm from "@/components/marketing/ContactForm"; import ContactForm from "@/components/marketing/ContactForm";
import { siteConfig } from "@/lib/site-config";
interface LocalSEOPageProps { interface LocalSEOPageProps {
ville: string; ville: string;
@@ -14,13 +15,13 @@ interface LocalSEOPageProps {
distanceMouchin?: string; distanceMouchin?: string;
} }
const services = [ // Services dérivés de siteConfig (sans "conseil" qui redirige vers /contact)
{ icon: "🏠", label: "Construction de maison", href: "/construction-maison" }, const services = siteConfig.footerServicesNav.map((s) => {
{ icon: "🔨", label: "Rénovation", href: "/renovation" }, const found = siteConfig.services.find(
{ icon: "💧", label: "Assainissement", href: "/assainissement" }, (sc) => sc.title === s.label || `/${sc.slug}` === s.href
{ icon: "🚧", label: "Création d'accès", href: "/creation-acces" }, );
{ icon: "🏗️", label: "Démolition", href: "/demolition" }, return { icon: found?.icon ?? "🔧", label: s.label, href: s.href };
]; });
export default function LocalSEOPage({ export default function LocalSEOPage({
ville, ville,
@@ -31,6 +32,8 @@ export default function LocalSEOPage({
texteLocal, texteLocal,
distanceMouchin, distanceMouchin,
}: LocalSEOPageProps) { }: LocalSEOPageProps) {
const { phone, phoneRaw, address } = siteConfig;
return ( return (
<main id="main-content" className="min-h-screen"> <main id="main-content" className="min-h-screen">
<Navbar /> <Navbar />
@@ -57,8 +60,8 @@ export default function LocalSEOPage({
<Link href="/contact" className="inline-flex items-center justify-center gap-2 bg-orange hover:bg-orange-hover text-white font-bold px-7 py-3.5 rounded-xl transition-colors pulse-glow"> <Link href="/contact" className="inline-flex items-center justify-center gap-2 bg-orange hover:bg-orange-hover text-white font-bold px-7 py-3.5 rounded-xl transition-colors pulse-glow">
Demander un devis gratuit Demander un devis gratuit
</Link> </Link>
<a href="tel:0674453089" className="inline-flex items-center justify-center gap-2 bg-white/10 hover:bg-white/20 text-white font-semibold px-7 py-3.5 rounded-xl transition-colors border border-white/20"> <a href={`tel:${phoneRaw}`} className="inline-flex items-center justify-center gap-2 bg-white/10 hover:bg-white/20 text-white font-semibold px-7 py-3.5 rounded-xl transition-colors border border-white/20">
06 74 45 30 89 {phone}
</a> </a>
</div> </div>
</ScrollReveal> </ScrollReveal>
@@ -116,9 +119,9 @@ export default function LocalSEOPage({
</div> </div>
<div> <div>
<p className="text-navy font-bold text-sm">Benoît Colin OBC Maçonnerie</p> <p className="text-navy font-bold text-sm">Benoît Colin OBC Maçonnerie</p>
<p className="text-text-muted text-xs">221 Route de Saint-Amand, 59310 Mouchin</p> <p className="text-text-muted text-xs">{address}</p>
<a href="tel:0674453089" className="text-orange font-bold text-sm hover:underline"> <a href={`tel:${phoneRaw}`} className="text-orange font-bold text-sm hover:underline">
06 74 45 30 89 {phone}
</a> </a>
</div> </div>
</div> </div>

View File

@@ -2,16 +2,11 @@
import { useState } from "react"; import { useState } from "react";
import Link from "next/link"; import Link from "next/link";
import { siteConfig } from "@/lib/site-config";
const navLinks = [
{ href: "/services", label: "Nos services" },
{ href: "/realisations", label: "Réalisations" },
{ href: "/partenaires", label: "Partenaires" },
{ href: "/contact", label: "Contact" },
];
export default function Navbar() { export default function Navbar() {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const { name, phone, phoneRaw, nav } = siteConfig;
return ( return (
<nav <nav
@@ -22,9 +17,9 @@ export default function Navbar() {
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16"> <div className="flex items-center justify-between h-16">
{/* Logo */} {/* Logo */}
<Link href="/" className="flex items-center gap-2.5" aria-label="OBC Maçonnerie - Accueil"> <Link href="/" className="flex items-center gap-2.5" aria-label={`${name} - Accueil`}>
<div className="w-9 h-9 bg-navy rounded-lg flex items-center justify-center shrink-0"> <div className="w-9 h-9 bg-navy rounded-lg flex items-center justify-center shrink-0">
<span className="text-white font-bold text-sm">OBC</span> <span className="text-white font-bold text-xs">OBC</span>
</div> </div>
<div className="flex flex-col leading-tight"> <div className="flex flex-col leading-tight">
<span className="text-navy font-bold text-sm leading-none">OBC</span> <span className="text-navy font-bold text-sm leading-none">OBC</span>
@@ -34,7 +29,7 @@ export default function Navbar() {
{/* Desktop links */} {/* Desktop links */}
<div className="hidden md:flex items-center gap-6"> <div className="hidden md:flex items-center gap-6">
{navLinks.map((link) => ( {nav.map((link) => (
<Link <Link
key={link.href} key={link.href}
href={link.href} href={link.href}
@@ -48,13 +43,13 @@ export default function Navbar() {
{/* CTA desktop */} {/* CTA desktop */}
<div className="hidden md:block"> <div className="hidden md:block">
<a <a
href="tel:0674453089" href={`tel:${phoneRaw}`}
className="inline-flex items-center gap-2 bg-orange text-white font-bold text-sm px-5 py-2.5 rounded-xl hover:bg-orange-hover transition-colors pulse-glow" className="inline-flex items-center gap-2 bg-orange text-white font-bold text-sm px-5 py-2.5 rounded-xl hover:bg-orange-hover transition-colors"
> >
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg> </svg>
06 74 45 30 89 {phone}
</a> </a>
</div> </div>
@@ -80,7 +75,7 @@ export default function Navbar() {
{/* Mobile menu */} {/* Mobile menu */}
{open && ( {open && (
<div className="md:hidden border-t border-border py-4 space-y-1"> <div className="md:hidden border-t border-border py-4 space-y-1">
{navLinks.map((link) => ( {nav.map((link) => (
<Link <Link
key={link.href} key={link.href}
href={link.href} href={link.href}
@@ -92,14 +87,14 @@ export default function Navbar() {
))} ))}
<div className="pt-2"> <div className="pt-2">
<a <a
href="tel:0674453089" href={`tel:${phoneRaw}`}
onClick={() => setOpen(false)} onClick={() => setOpen(false)}
className="flex items-center justify-center gap-2 bg-orange text-white font-bold text-sm px-5 py-3 rounded-xl mt-2" className="flex items-center justify-center gap-2 bg-orange text-white font-bold text-sm px-5 py-3 rounded-xl mt-2"
> >
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg> </svg>
Appeler Benoît 06 74 45 30 89 {phone}
</a> </a>
</div> </div>
</div> </div>

85
lib/content.ts Normal file
View File

@@ -0,0 +1,85 @@
// lib/content.ts
// Couche d'abstraction entre les données et les composants.
//
// AUJOURD'HUI : lit siteConfig (données statiques en JSON)
// DEMAIN : remplacer chaque return par un appel Payload CMS
// Les composants n'ont JAMAIS à changer.
//
// Exemple de migration future :
// - return siteConfig.services
// + return await payload.find({ collection: 'services' }).then(r => r.docs)
import { siteConfig } from "./site-config";
import type {
Service,
Realisation,
Testimonial,
FAQItem,
BlogPost,
Partner,
NavItem,
} from "@/types/content";
// ── Navigation ────────────────────────────────────────────────
export async function getNavItems(): Promise<NavItem[]> {
// FUTURE: return await payload.findGlobal({ slug: 'navigation' })
return [...siteConfig.nav];
}
// ── Config globale ────────────────────────────────────────────
export async function getSiteConfig() {
// FUTURE: return await payload.findGlobal({ slug: 'site-settings' })
return siteConfig;
}
// ── Services ──────────────────────────────────────────────────
export async function getServices(): Promise<Service[]> {
// FUTURE: return await payload.find({ collection: 'services', sort: 'order' }).then(r => r.docs)
return [...siteConfig.services];
}
export async function getServiceBySlug(slug: string): Promise<Service | null> {
// FUTURE: return await payload.find({ collection: 'services', where: { slug: { equals: slug } } }).then(r => r.docs[0] ?? null)
return siteConfig.services.find((s) => s.slug === slug) ?? null;
}
// ── Réalisations ──────────────────────────────────────────────
export async function getRealisations(): Promise<Realisation[]> {
// FUTURE: return await payload.find({ collection: 'realisations', sort: '-publishedAt' }).then(r => r.docs)
return [...siteConfig.realisations];
}
// ── Témoignages ───────────────────────────────────────────────
export async function getTestimonials(): Promise<Testimonial[]> {
// FUTURE: return await payload.find({ collection: 'testimonials', where: { featured: { equals: true } } }).then(r => r.docs)
return [...siteConfig.testimonials];
}
// ── FAQ ───────────────────────────────────────────────────────
export async function getFAQ(): Promise<FAQItem[]> {
// FUTURE: return await payload.find({ collection: 'faq', sort: 'order' }).then(r => r.docs)
return [...siteConfig.faq];
}
// ── Partenaires ───────────────────────────────────────────────
export async function getPartners(): Promise<Partner[]> {
// FUTURE: return await payload.find({ collection: 'partners', sort: 'order' }).then(r => r.docs)
return [...siteConfig.partners];
}
// ── Blog ──────────────────────────────────────────────────────
export async function getBlogPosts(): Promise<BlogPost[]> {
// FUTURE: return await payload.find({ collection: 'articles', where: { status: { equals: 'published' } }, sort: '-publishedAt' }).then(r => r.docs)
return [...siteConfig.blogPosts];
}
export async function getBlogPost(slug: string): Promise<BlogPost | null> {
// FUTURE: return await payload.find({ collection: 'articles', where: { slug: { equals: slug } } }).then(r => r.docs[0] ?? null)
return siteConfig.blogPosts.find((p) => p.slug === slug) ?? null;
}
// ── Valeurs / Piliers ─────────────────────────────────────────
export async function getValues() {
// FUTURE: return await payload.find({ collection: 'values', sort: 'order' }).then(r => r.docs)
return [...siteConfig.values];
}

380
lib/site-config.ts Normal file
View File

@@ -0,0 +1,380 @@
// lib/site-config.ts
// Source unique de vérité pour toutes les données du site OBC Maçonnerie.
// Toute modification de contenu (textes, services, FAQ...) se fait ICI.
// Les composants consomment ces données via lib/content.ts — jamais en dur.
export const siteConfig = {
name: "OBC Maçonnerie",
dirigeant: "Benoît Colin",
phone: "06 74 45 30 89",
phoneRaw: "0674453089",
email: "contact@obc-terrassement.fr",
address: "221 Route de Saint-Amand, 59310 Mouchin",
addressLine1: "221 Route de Saint-Amand",
addressLocality: "Mouchin",
addressPostalCode: "59310",
addressRegion: "Hauts-de-France",
addressCountry: "FR",
siren: "531827871",
url: "https://obc-terrassement.fr",
zones: [
"Orchies",
"Mouchin",
"Flines-lès-Raches",
"Château-l'Abbaye",
"Mérignies",
"Douai",
"Valenciennes",
"Saint-Amand-les-Eaux",
],
zoneDescription: "20 à 30 km autour de Mouchin (Nord 59)",
nav: [
{ label: "Accueil", href: "/" },
{ label: "Services", href: "/services" },
{ label: "Réalisations", href: "/realisations" },
{ label: "Blog", href: "/blog" },
{ label: "Contact", href: "/contact" },
],
footerServicesNav: [
{ label: "Construction de maison", href: "/construction-maison" },
{ label: "Rénovation", href: "/renovation" },
{ label: "Assainissement", href: "/assainissement" },
{ label: "Création d'accès", href: "/creation-acces" },
{ label: "Démolition", href: "/demolition" },
],
footerMainNav: [
{ label: "Accueil", href: "/" },
{ label: "Réalisations", href: "/realisations" },
{ label: "Partenaires", href: "/partenaires" },
{ label: "Contact", href: "/contact" },
{ label: "Blog", href: "/blog" },
],
footerLegalNav: [
{ label: "Mentions légales", href: "/mentions-legales" },
{ label: "Confidentialité", href: "/confidentialite" },
{ label: "CGV", href: "/cgv" },
],
seo: {
title: "OBC Maçonnerie | Constructeur & Maçon à Orchies (Nord 59)",
description:
"Benoît Colin, maçon expert à Mouchin. Construction de maison, rénovation, assainissement et gros œuvre dans un rayon de 30km autour d'Orchies. Devis gratuit.",
},
hero: {
title: "Maçon & Constructeur dans le Nord",
subtitle:
"Construction de maison, rénovation, assainissement et gros œuvre — expertise autour d'Orchies",
cta: "Demander un devis gratuit",
ctaSecondary: "Voir nos réalisations",
badge:
"Disponible, à l'écoute, Benoît vous accompagne de la première pierre à la remise des clés",
stats: [
{ val: "15+", label: "ans d'expérience" },
{ val: "200+", label: "chantiers réalisés" },
{ val: "30km", label: "de rayon d'action" },
],
},
services: [
{
slug: "construction-maison",
title: "Construction de maison",
shortDescription:
"Fondations, ossature bois, gros œuvre — on bâtit votre projet de A à Z avec vous.",
longDescription:
"De la conception des fondations à la pose du toit, Benoît vous accompagne à chaque étape de votre construction. Maison individuelle, ossature bois, béton banché — chaque projet est unique.",
icon: "🏠",
keywords: [
"construction maison Nord",
"maçon construction maison Orchies",
"fondation ossature bois Nord",
],
},
{
slug: "renovation",
title: "Rénovation",
shortDescription:
"Maison ou appartement, on s'adapte à votre projet et vos envies.",
longDescription:
"Rénovation partielle ou complète, intérieure et extérieure. Benoît adapte chaque projet à votre budget et vos envies. Restructuration, création d'ouvertures, extension.",
icon: "🔨",
keywords: [
"rénovation maison Nord 59",
"maçon rénovation Douai",
"maçon rénovation Valenciennes",
],
},
{
slug: "assainissement",
title: "Assainissement",
shortDescription:
"Mise aux normes et création de systèmes d'assainissement fiables.",
longDescription:
"Installation et mise aux normes de vos systèmes d'assainissement individuel et collectif. Fosse toutes eaux, micro-station, épandage — dans les règles de l'art.",
icon: "💧",
keywords: [
"assainissement maison Nord",
"assainissement individuel Nord 59",
],
},
{
slug: "creation-acces",
title: "Création d'accès",
shortDescription: "Voiries, entrées, chemins — on crée vos accès sur mesure.",
longDescription:
"Création de voiries, entrées de propriété et chemins d'accès. Béton, béton imprimé, pavés, gravier stabilisé — travail soigné et durable.",
icon: "🚧",
keywords: [
"création accès maison Nord",
"voirie entrée propriété Nord 59",
],
},
{
slug: "demolition",
title: "Démolition",
shortDescription:
"Démolition totale ou partielle, avec toutes les garanties de sécurité.",
longDescription:
"Démolition de bâtiments, murs et ouvrages en toute sécurité. Évacuation des gravats incluse. Préparation du terrain pour votre future construction.",
icon: "🏗️",
keywords: [
"démolition maison Nord 59",
"démolition bâtiment Nord",
],
},
{
slug: "conseil",
title: "Conseil & Accompagnement",
shortDescription:
"Benoît vous éclaire dans vos choix : matériaux, plans, adaptations — on réfléchit ensemble.",
longDescription:
"Un seul interlocuteur pour tout votre projet. Benoît analyse votre plan, vous conseille sur les matériaux et coordonne les artisans partenaires.",
icon: "🤝",
keywords: [
"entrepreneur maçon Nord 59",
"gros œuvre Nord",
],
},
],
values: [
{
title: "Proche de vous",
description:
"Disponible, à l'écoute. Benoît se déplace sur votre chantier pour comprendre votre projet et vous proposer les meilleures solutions.",
icon: "📍",
},
{
title: "Conseil expert",
description:
"Il vous guide dans vos choix de matériaux, adapte les plans d'architecte, suggère des améliorations auxquelles vous n'avez pas pensé.",
icon: "💡",
},
{
title: "Acteur de confiance",
description:
"Transparent à chaque étape, il vous rassure et vous explique chaque décision. Pas de surprise, pas de mauvaise nouvelle.",
icon: "🛡️",
},
{
title: "Passionné du métier",
description:
"On ne fait jamais deux fois la même maison. Benoît réfléchit avec vous pour que le résultat vous ressemble.",
icon: "❤️",
},
],
partners: [
{ label: "Électricité", icon: "⚡", desc: "Installation électrique aux normes NF C 15-100, tableau de distribution, prises, éclairage." },
{ label: "Plomberie", icon: "🔧", desc: "Plomberie sanitaire, chauffage central, installation de salles de bains et cuisines." },
{ label: "Charpente", icon: "🪵", desc: "Charpente traditionnelle ou industrielle, structure bois pour combles aménageables ou non." },
{ label: "Couverture", icon: "🏚️", desc: "Pose de toiture, tuiles, ardoises, zinc — étanchéité et finitions soignées." },
{ label: "Isolation", icon: "🧱", desc: "Isolation thermique et phonique par l'intérieur ou l'extérieur, combles, planchers." },
{ label: "Menuiserie", icon: "🚪", desc: "Fenêtres, portes, vérandas, volets — menuiserie bois, PVC ou aluminium." },
{ label: "Carrelage", icon: "🔳", desc: "Pose de carrelage, parquet, faïence — pour sols et murs, intérieur et extérieur." },
{ label: "Peinture", icon: "🎨", desc: "Peinture intérieure et extérieure, enduits décoratifs, ravalement de façade." },
],
partnersTitle: "Seul on va vite, ensemble on va plus loin.",
partnersMessage:
"Grâce à notre réseau de partenaires de confiance, nous coordonnons l'ensemble des corps de métier pour que votre maison prenne forme de A à Z.",
faq: [
{
question: "Dans quelle zone intervenez-vous ?",
answer:
"Nous intervenons dans un rayon de 20 à 30 km autour de Mouchin (59310) : Orchies, Flines-lès-Raches, Château-l'Abbaye, Mérignies, Douai, Valenciennes, Saint-Amand-les-Eaux et les communes alentour.",
},
{
question: "Faites-vous des devis gratuits ?",
answer:
"Oui, le devis est gratuit et sans engagement. Benoît se déplace sur votre chantier pour évaluer votre projet et vous remettre une estimation détaillée.",
},
{
question: "Pouvez-vous adapter un plan d'architecte ?",
answer:
"Absolument. C'est même l'une de nos spécialités. Benoît analyse votre plan et vous suggère des adaptations pour optimiser l'espace, le rendu final et votre budget.",
},
{
question: "Combien de temps dure une construction de maison ?",
answer:
"En moyenne, comptez 10 à 18 mois pour une construction neuve selon la complexité du projet et les délais de livraison des matériaux. Benoît vous donnera un calendrier précis lors du devis.",
},
{
question: "Travaillez-vous avec d'autres artisans ?",
answer:
"Oui. Nous coordonnons un réseau de partenaires de confiance (électriciens, plombiers, charpentiers, couvreurs, menuisiers, carreleurs, peintres) pour vous livrer une maison complète avec un seul interlocuteur.",
},
],
testimonials: [
{
name: "Christophe & Marie L.",
ville: "Orchies",
service: "construction-maison",
text: "Benoît nous a accompagnés de A à Z dans la construction de notre maison. Il a su adapter le plan d'architecte à nos envies tout en respectant notre budget. Disponible, professionnel, et vraiment à l'écoute. On recommande les yeux fermés.",
rating: 5,
},
{
name: "Sophie D.",
ville: "Douai",
service: "renovation",
text: "On lui a confié la rénovation complète de notre maison de 1970. Benoît a pris le temps de tout nous expliquer, a proposé des solutions auxquelles on n'avait pas pensé, et le résultat est magnifique. Un vrai professionnel.",
rating: 5,
},
{
name: "Famille Moreau",
ville: "Saint-Amand-les-Eaux",
service: "assainissement",
text: "Mise aux normes de notre système d'assainissement réalisée dans les délais et en toute transparence. Benoît nous a expliqué chaque étape. Très sérieux et propre dans son travail.",
rating: 5,
},
],
realisations: [
{
title: "Maison individuelle à Orchies",
ville: "Orchies (59)",
service: "construction-maison",
categorie: "Construction neuve",
description:
"Construction d'une maison de 130 m² — fondations, gros œuvre, dalle béton et ossature.",
color: "bg-navy",
image: "/images/realisations/chantier-1.jpg",
},
{
title: "Rénovation complète à Douai",
ville: "Douai (59)",
service: "renovation",
categorie: "Rénovation",
description:
"Restructuration intérieure complète d'une maison de ville : abattage de cloisons, création d'un escalier neuf, doublages.",
color: "bg-stone",
image: "/images/realisations/chantier-2.jpg",
},
{
title: "Mise aux normes à Saint-Amand",
ville: "Saint-Amand-les-Eaux (59)",
service: "assainissement",
categorie: "Assainissement",
description:
"Remplacement d'une fosse septique vétuste par une micro-station d'épuration conforme aux normes.",
color: "bg-navy-light",
image: "/images/realisations/chantier-3.jpg",
},
{
title: "Entrée en béton imprimé à Mérignies",
ville: "Mérignies (59)",
service: "creation-acces",
categorie: "Création d'accès",
description:
"Création d'une entrée de propriété en béton imprimé effet pavés, avec caniveau de drainage.",
color: "bg-orange",
image: "/images/realisations/chantier-4.jpg",
},
{
title: "Extension ossature bois à Flines",
ville: "Flines-lès-Raches (59)",
service: "construction-maison",
categorie: "Construction neuve",
description:
"Agrandissement d'une maison existante par extension ossature bois, fondations et dalle.",
color: "bg-navy",
image: "/images/realisations/chantier-5.jpg",
},
{
title: "Démolition & reconstruction à Valenciennes",
ville: "Valenciennes (59)",
service: "demolition",
categorie: "Démolition",
description:
"Démolition d'un bâtiment annexe et curage d'une grange pour préparer une rénovation complète.",
color: "bg-stone",
image: "/images/realisations/chantier-6.jpg",
},
],
blogPosts: [
{
slug: "combien-coute-construction-maison-nord",
titre: "Combien coûte la construction d'une maison dans le Nord en 2025 ?",
extrait:
"Budget, matériaux, terrain, main-d'œuvre — tout ce qu'il faut savoir pour estimer le coût de votre construction neuve dans le Nord.",
cat: "Construction",
date: "15 février 2025",
readTime: "6 min",
},
{
slug: "etapes-renovation-maison-ancienne",
titre: "Les étapes clés d'une rénovation de maison ancienne",
extrait:
"Vous avez acheté une maison ancienne dans le Nord et vous voulez la rénover ? Voici les étapes indispensables pour réussir votre projet.",
cat: "Rénovation",
date: "8 janvier 2025",
readTime: "5 min",
},
{
slug: "assainissement-non-collectif-obligations",
titre: "Assainissement non collectif : vos obligations légales",
extrait:
"Contrôle SPANC, mise aux normes, vente immobilière — tout ce que vous devez savoir sur l'assainissement non collectif.",
cat: "Assainissement",
date: "20 décembre 2024",
readTime: "4 min",
},
{
slug: "ossature-bois-avantages",
titre: "Ossature bois : pourquoi choisir ce mode constructif ?",
extrait:
"Légèreté, performance thermique, rapidité de construction — l'ossature bois a de nombreux avantages. OBC Maçonnerie vous explique.",
cat: "Construction",
date: "5 novembre 2024",
readTime: "5 min",
},
{
slug: "travaux-renovation-sans-permis-construction",
titre: "Quels travaux de rénovation ne nécessitent pas de permis ?",
extrait:
"Permis de construire, déclaration préalable, simple déclaration — on vous explique les règles selon la nature de vos travaux.",
cat: "Rénovation",
date: "18 octobre 2024",
readTime: "4 min",
},
{
slug: "fondations-maison-quels-types",
titre: "Les différents types de fondations pour une maison",
extrait:
"Semelles filantes, radier, pieux — quelles fondations choisir selon votre terrain et votre projet de construction ?",
cat: "Construction",
date: "2 septembre 2024",
readTime: "5 min",
},
],
} as const;
export type SiteConfig = typeof siteConfig;

54
payload/README.md Normal file
View File

@@ -0,0 +1,54 @@
# Payload CMS — Préparation
Ce dossier contient les schémas Payload CMS commentés, prêts à être activés
quand le projet bascule de contenu statique vers un CMS administrable.
## Architecture cible
```
payload/
├── collections/
│ ├── Articles.ts → Articles de blog
│ ├── Realisations.ts → Galerie chantiers
│ ├── Services.ts → Services proposés
│ ├── Testimonials.ts → Avis clients
│ └── FAQ.ts → Questions fréquentes
└── globals/
└── SiteSettings.ts → Paramètres globaux du site
```
## Migration (3 étapes)
### Étape 1 — Installer Payload CMS
```bash
npm install payload @payloadcms/db-postgres @payloadcms/richtext-lexical
```
### Étape 2 — Activer les collections
Décommenter le code dans chaque fichier `collections/*.ts` et `globals/*.ts`,
puis créer `payload.config.ts` à la racine :
```ts
import { buildConfig } from 'payload'
import { Services } from './payload/collections/Services'
import { Realisations } from './payload/collections/Realisations'
import { Articles } from './payload/collections/Articles'
import { Testimonials } from './payload/collections/Testimonials'
import { FAQ } from './payload/collections/FAQ'
import { SiteSettings } from './payload/globals/SiteSettings'
export default buildConfig({
collections: [Services, Realisations, Articles, Testimonials, FAQ],
globals: [SiteSettings],
// ...db, admin, etc.
})
```
### Étape 3 — Mettre à jour lib/content.ts
Remplacer les `return siteConfig.xxx` par les appels Payload commentés.
**Les composants n'ont pas à changer.**
## Données actuelles
Toutes les données sont dans `lib/site-config.ts`.
Les composants les consomment via `lib/content.ts`.

View File

@@ -0,0 +1,94 @@
// payload/collections/Articles.ts
// Schéma Payload CMS pour les articles de blog.
// COMMENTÉ — activé lors de la migration vers Payload.
/*
import type { CollectionConfig } from 'payload'
import { lexicalEditor } from '@payloadcms/richtext-lexical'
export const Articles: CollectionConfig = {
slug: 'articles',
admin: {
useAsTitle: 'title',
defaultColumns: ['title', 'category', 'status', 'publishedAt'],
},
fields: [
{
name: 'title',
type: 'text',
required: true,
label: 'Titre de l\'article',
},
{
name: 'slug',
type: 'text',
required: true,
unique: true,
label: 'URL (slug)',
admin: {
description: 'ex: combien-coute-construction-maison-nord',
},
},
{
name: 'excerpt',
type: 'textarea',
label: 'Extrait (meta description)',
},
{
name: 'content',
type: 'richText',
editor: lexicalEditor(),
label: 'Contenu complet',
},
{
name: 'category',
type: 'select',
options: [
{ label: 'Construction', value: 'construction' },
{ label: 'Rénovation', value: 'renovation' },
{ label: 'Assainissement', value: 'assainissement' },
{ label: 'Conseils', value: 'conseils' },
],
label: 'Catégorie',
},
{
name: 'publishedAt',
type: 'date',
label: 'Date de publication',
},
{
name: 'status',
type: 'select',
options: [
{ label: 'Brouillon', value: 'draft' },
{ label: 'Publié', value: 'published' },
],
defaultValue: 'draft',
label: 'Statut',
},
{
name: 'seoTitle',
type: 'text',
label: 'Titre SEO (optionnel — remplace title)',
},
{
name: 'seoDescription',
type: 'textarea',
label: 'Description SEO (optionnel — remplace excerpt)',
},
],
}
*/
export type PayloadArticle = {
id: string;
slug: string;
title: string;
excerpt: string;
content: unknown; // Lexical rich text
category: string;
publishedAt: string;
status: "draft" | "published";
seoTitle?: string;
seoDescription?: string;
};

View File

@@ -0,0 +1,42 @@
// payload/collections/FAQ.ts
// Schéma Payload CMS pour la FAQ.
// COMMENTÉ — activé lors de la migration vers Payload.
/*
import type { CollectionConfig } from 'payload'
export const FAQ: CollectionConfig = {
slug: 'faq',
admin: {
useAsTitle: 'question',
defaultColumns: ['question', 'order'],
},
fields: [
{
name: 'question',
type: 'text',
required: true,
label: 'Question',
},
{
name: 'answer',
type: 'textarea',
required: true,
label: 'Réponse',
},
{
name: 'order',
type: 'number',
label: 'Ordre d\'affichage',
defaultValue: 0,
},
],
}
*/
export type PayloadFAQItem = {
id: string;
question: string;
answer: string;
order: number;
};

View File

@@ -0,0 +1,84 @@
// payload/collections/Realisations.ts
// Schéma Payload CMS pour les réalisations / portfolio.
// COMMENTÉ — activé lors de la migration vers Payload.
/*
import type { CollectionConfig } from 'payload'
export const Realisations: CollectionConfig = {
slug: 'realisations',
admin: {
useAsTitle: 'title',
defaultColumns: ['title', 'ville', 'service', 'publishedAt'],
},
fields: [
{
name: 'title',
type: 'text',
required: true,
label: 'Titre du chantier',
},
{
name: 'ville',
type: 'text',
required: true,
label: 'Ville',
},
{
name: 'service',
type: 'relationship',
relationTo: 'services',
label: 'Service associé',
},
{
name: 'description',
type: 'textarea',
label: 'Description courte',
},
{
name: 'image',
type: 'upload',
relationTo: 'media',
label: 'Photo principale',
},
{
name: 'gallery',
type: 'array',
label: 'Galerie photos',
fields: [
{
name: 'image',
type: 'upload',
relationTo: 'media',
},
{
name: 'caption',
type: 'text',
},
],
},
{
name: 'featured',
type: 'checkbox',
label: 'Mettre en avant (page accueil)',
defaultValue: false,
},
{
name: 'publishedAt',
type: 'date',
label: 'Date du chantier',
},
],
}
*/
export type PayloadRealisation = {
id: string;
title: string;
ville: string;
service: string;
description: string;
image: string;
featured: boolean;
publishedAt: string;
};

View File

@@ -0,0 +1,78 @@
// payload/collections/Services.ts
// Schéma Payload CMS pour les services OBC Maçonnerie.
// COMMENTÉ — activé lors de la migration vers Payload.
//
// Pour activer : décommenter et importer dans payload.config.ts
/*
import type { CollectionConfig } from 'payload'
export const Services: CollectionConfig = {
slug: 'services',
admin: {
useAsTitle: 'title',
defaultColumns: ['title', 'slug', 'updatedAt'],
},
fields: [
{
name: 'slug',
type: 'text',
required: true,
unique: true,
admin: { description: 'Identifiant URL (ex: construction-maison)' },
},
{
name: 'title',
type: 'text',
required: true,
label: 'Titre du service',
},
{
name: 'shortDescription',
type: 'text',
required: true,
label: 'Description courte (carte home page)',
},
{
name: 'longDescription',
type: 'textarea',
label: 'Description longue (page dédiée)',
},
{
name: 'icon',
type: 'text',
label: 'Emoji icône',
admin: { description: 'ex: 🏠' },
},
{
name: 'keywords',
type: 'array',
label: 'Mots-clés SEO',
fields: [
{
name: 'keyword',
type: 'text',
},
],
},
{
name: 'order',
type: 'number',
label: 'Ordre d\'affichage',
defaultValue: 0,
},
],
}
*/
// Type exporté pour l'autocomplétion — reste actif même sans Payload
export type PayloadService = {
id: string;
slug: string;
title: string;
shortDescription: string;
longDescription: string;
icon: string;
keywords: { keyword: string }[];
order: number;
};

View File

@@ -0,0 +1,64 @@
// payload/collections/Testimonials.ts
// Schéma Payload CMS pour les témoignages clients.
// COMMENTÉ — activé lors de la migration vers Payload.
/*
import type { CollectionConfig } from 'payload'
export const Testimonials: CollectionConfig = {
slug: 'testimonials',
admin: {
useAsTitle: 'name',
defaultColumns: ['name', 'ville', 'service', 'rating', 'featured'],
},
fields: [
{
name: 'name',
type: 'text',
required: true,
label: 'Nom du client',
},
{
name: 'ville',
type: 'text',
label: 'Ville',
},
{
name: 'service',
type: 'relationship',
relationTo: 'services',
label: 'Service concerné',
},
{
name: 'text',
type: 'textarea',
required: true,
label: 'Témoignage',
},
{
name: 'rating',
type: 'number',
min: 1,
max: 5,
defaultValue: 5,
label: 'Note (1 à 5)',
},
{
name: 'featured',
type: 'checkbox',
label: 'Afficher sur la page d\'accueil',
defaultValue: true,
},
],
}
*/
export type PayloadTestimonial = {
id: string;
name: string;
ville: string;
service: string;
text: string;
rating: number;
featured: boolean;
};

View File

@@ -0,0 +1,95 @@
// payload/globals/SiteSettings.ts
// Schéma Payload CMS pour les paramètres globaux du site.
// COMMENTÉ — activé lors de la migration vers Payload.
//
// Permet à Benoît (ou à un admin) de modifier les infos
// de contact, le texte du hero, etc. sans toucher au code.
/*
import type { GlobalConfig } from 'payload'
export const SiteSettings: GlobalConfig = {
slug: 'site-settings',
admin: {
group: 'Paramètres',
},
fields: [
// ── Informations de contact ───────────────────────────────
{
name: 'contact',
type: 'group',
label: 'Contact',
fields: [
{ name: 'phone', type: 'text', label: 'Téléphone' },
{ name: 'email', type: 'email', label: 'Email' },
{ name: 'address', type: 'text', label: 'Adresse complète' },
],
},
// ── Hero page d'accueil ───────────────────────────────────
{
name: 'hero',
type: 'group',
label: 'Section Hero (page d\'accueil)',
fields: [
{ name: 'title', type: 'text', label: 'Titre H1' },
{ name: 'subtitle', type: 'textarea', label: 'Sous-titre' },
{ name: 'badge', type: 'text', label: 'Badge/accroche' },
{ name: 'cta', type: 'text', label: 'CTA principal' },
{ name: 'ctaSecondary', type: 'text', label: 'CTA secondaire' },
],
},
// ── Zone d'intervention ────────────────────────────────────
{
name: 'zones',
type: 'array',
label: 'Villes d\'intervention',
fields: [
{ name: 'city', type: 'text', label: 'Ville' },
],
},
{
name: 'zoneDescription',
type: 'text',
label: 'Description zone (ex: 20 à 30 km autour de Mouchin)',
},
// ── SEO global ────────────────────────────────────────────
{
name: 'seo',
type: 'group',
label: 'SEO global',
fields: [
{ name: 'title', type: 'text', label: 'Titre SEO par défaut' },
{ name: 'description', type: 'textarea', label: 'Description SEO par défaut' },
],
},
// ── Message partenaires ───────────────────────────────────
{
name: 'partnersMessage',
type: 'textarea',
label: 'Message section partenaires',
},
],
}
*/
export type PayloadSiteSettings = {
contact: {
phone: string;
email: string;
address: string;
};
hero: {
title: string;
subtitle: string;
badge: string;
cta: string;
ctaSecondary: string;
};
zones: { city: string }[];
zoneDescription: string;
seo: {
title: string;
description: string;
};
partnersMessage: string;
};

62
types/content.ts Normal file
View File

@@ -0,0 +1,62 @@
// types/content.ts
// Types partagés entre lib/content.ts et les composants.
// Ces types correspondent exactement aux schémas Payload CMS futurs
// définis dans /payload/collections/*.ts
export type NavItem = {
label: string;
href: string;
};
export type Service = {
slug: string;
title: string;
shortDescription: string;
longDescription: string;
icon: string;
keywords: string[];
};
export type Realisation = {
title: string;
ville: string;
service: string;
categorie: string;
description: string;
color: string;
image: string;
};
export type Testimonial = {
name: string;
ville: string;
service: string;
text: string;
rating: number;
};
export type FAQItem = {
question: string;
answer: string;
};
export type Partner = {
label: string;
icon: string;
desc: string;
};
export type BlogPost = {
slug: string;
titre: string;
extrait: string;
cat: string;
date: string;
readTime: string;
};
export type Value = {
title: string;
description: string;
icon: string;
};