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

@@ -3,65 +3,23 @@ import Link from "next/link";
import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal";
import { getServices, getSiteConfig } from "@/lib/content";
export const metadata: Metadata = {
title: "Nos Services | Construction, Rénovation, Assainissement",
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.",
alternates: { canonical: "https://obc-maconnerie.fr/services" },
alternates: { canonical: "https://obc-terrassement.fr/services" },
};
const services = [
{
icon: "🏠",
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 async function ServicesPage() {
const [services, config] = await Promise.all([getServices(), getSiteConfig()]);
const { phone, phoneRaw } = config;
export default function ServicesPage() {
return (
<main id="main-content" className="min-h-screen">
<Navbar />
{/* Hero */}
<section className="bg-navy py-16 md:py-20">
<div className="max-w-4xl mx-auto px-4 sm:px-6 text-center">
<ScrollReveal direction="up">
@@ -74,61 +32,54 @@ export default function ServicesPage() {
</div>
</section>
{/* Services */}
<section className="py-16 md:py-20 bg-bg">
<div className="max-w-5xl mx-auto px-4 sm:px-6 space-y-8">
{services.map((s, i) => (
<ScrollReveal key={s.title} direction="up" delay={i * 60}>
<div className="bg-bg-white border border-border rounded-2xl p-6 md:p-8 flex flex-col md:flex-row gap-6">
<div className="text-5xl shrink-0">{s.icon}</div>
<div className="flex-1">
<h2 className="text-xl font-bold text-navy mb-2">{s.title}</h2>
<p className="text-text-light text-sm leading-relaxed mb-4">{s.desc}</p>
<div className="flex flex-wrap gap-2 mb-5">
{s.points.map((p) => (
<span key={p} className="bg-bg-muted text-text-light text-xs font-medium px-3 py-1 rounded-full">
{p}
</span>
))}
{services.map((s, i) => {
const href = s.slug === "conseil" ? "/contact" : `/${s.slug}`;
return (
<ScrollReveal key={s.slug} direction="up" delay={i * 60}>
<div className="bg-bg-white border border-border rounded-2xl p-6 md:p-8 flex flex-col md:flex-row gap-6">
<div className="text-5xl shrink-0">{s.icon}</div>
<div className="flex-1">
<h2 className="text-xl font-bold text-navy mb-2">{s.title}</h2>
<p className="text-text-light text-sm leading-relaxed mb-4">{s.longDescription}</p>
<div className="flex flex-wrap gap-2 mb-5">
{s.keywords.map((k) => (
<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>
<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>
</ScrollReveal>
))}
</ScrollReveal>
);
})}
</div>
</section>
{/* CTA */}
<section className="py-16 bg-stone-bg">
<div className="max-w-2xl mx-auto px-4 text-center">
<ScrollReveal direction="up">
<h2 className="text-2xl md:text-3xl font-bold text-navy mb-4">
Vous avez un projet ? Parlons-en.
</h2>
<h2 className="text-2xl md:text-3xl font-bold text-navy mb-4">Vous avez un projet ? Parlons-en.</h2>
<p className="text-text-light mb-6">
Benoît se déplace gratuitement pour évaluer votre projet et vous remettre un devis détaillé.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<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"
>
<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">
Demander un devis gratuit
</Link>
<a
href="tel:0674453089"
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 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">
{phone}
</a>
</div>
</ScrollReveal>