Files
obc-terrassement/app/contact/page.tsx
Claude 15c60a274c 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
2026-02-27 13:05:19 +00:00

126 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Metadata } from "next";
import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal";
import ContactForm from "@/components/marketing/ContactForm";
import { getSiteConfig } from "@/lib/content";
export async function generateMetadata(): Promise<Metadata> {
const config = await getSiteConfig();
return {
title: "Contact & Devis Gratuit | OBC Maçonnerie Nord",
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` },
};
}
export default async function ContactPage() {
const config = await getSiteConfig();
const { phone, phoneRaw, email, address, zones, zoneDescription } = config;
const infos = [
{
icon: "📞",
titre: "Téléphone",
val: phone,
href: `tel:${phoneRaw}`,
desc: "LunVen 7h19h",
},
{
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",
},
];
return (
<main id="main-content" className="min-h-screen">
<Navbar />
<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">
<span className="text-orange text-sm font-semibold uppercase tracking-widest">Devis gratuit</span>
<h1 className="text-3xl md:text-5xl font-bold text-white mt-2 mb-4">
Contactez OBC Maçonnerie
</h1>
<p className="text-white/70 text-lg max-w-xl mx-auto">
Benoît se déplace gratuitement pour évaluer votre projet et vous remettre un devis détaillé sous 24h.
</p>
</ScrollReveal>
</div>
</section>
<section className="py-16 md:py-20 bg-bg">
<div className="max-w-5xl mx-auto px-4 sm:px-6">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-10">
{/* Infos + zones */}
<div>
<ScrollReveal direction="left">
<h2 className="text-xl font-bold text-navy mb-6">Nos coordonnées</h2>
<div className="space-y-4 mb-8">
{infos.map((info) => (
<div key={info.titre} className="flex items-start gap-4 bg-bg-white border border-border rounded-xl p-4">
<span className="text-2xl shrink-0">{info.icon}</span>
<div>
<p className="text-navy font-semibold text-sm">{info.titre}</p>
{info.href ? (
<a href={info.href} className="text-orange font-bold hover:underline text-sm">
{info.val}
</a>
) : (
<p className="text-text-light text-sm">{info.val}</p>
)}
<p className="text-text-muted text-xs mt-0.5">{info.desc}</p>
</div>
</div>
))}
</div>
<h3 className="text-base font-bold text-navy mb-3">Zone d&apos;intervention</h3>
<div className="flex flex-wrap gap-2 mb-6">
{zones.map((z) => (
<span key={z} className="inline-flex items-center gap-1 bg-bg-white border border-border text-navy text-xs font-medium px-3 py-1.5 rounded-full">
<span className="text-orange">📍</span> {z}
</span>
))}
</div>
<p className="text-text-muted text-xs italic">
Et toutes les communes dans un rayon de {zoneDescription}.
</p>
<div className="mt-8 bg-navy rounded-2xl p-6">
<h3 className="text-white font-bold mb-2">Devis gratuit &amp; sans engagement</h3>
<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.
</p>
</div>
</ScrollReveal>
</div>
{/* Formulaire */}
<div>
<ScrollReveal direction="right">
<h2 className="text-xl font-bold text-navy mb-6">Votre demande de devis</h2>
<ContactForm />
</ScrollReveal>
</div>
</div>
</div>
</section>
<Footer />
</main>
);
}