Merge version_2_1781983054674 into main #1

Merged
bender merged 1 commits from version_2_1781983054674 into main 2026-06-20 19:21:08 +00:00
7 changed files with 390 additions and 166 deletions

View File

@@ -1,176 +1,30 @@
import AboutTextSplit from '@/components/sections/about/AboutTextSplit';
import ContactCta from '@/components/sections/contact/ContactCta';
import FeaturesArrowCards from '@/components/sections/features/FeaturesArrowCards';
import HeroBillboard from '@/components/sections/hero/HeroBillboard';
import PricingCenteredCards from '@/components/sections/pricing/PricingCenteredCards';
import TestimonialQuoteCards from '@/components/sections/testimonial/TestimonialQuoteCards';
import SectionErrorBoundary from "@/components/ui/SectionErrorBoundary";
// AUTO-GENERATED shell by per-section-migrate.
// Section bodies live in ./<PageBase>/sections/<X>.tsx. Edit the section
// files directly. Non-block content (wrappers, non-inlinable sections) is
// preserved inline; extracted section blocks become <XSection/> refs.
export default function HomePage() {
import React from 'react';
import HeroSection from './HomePage/sections/Hero';
import AboutSection from './HomePage/sections/About';
import FeaturesSection from './HomePage/sections/Features';
import PricingSection from './HomePage/sections/Pricing';
import TestimonialsSection from './HomePage/sections/Testimonials';
import ContactSection from './HomePage/sections/Contact';
export default function HomePage(): React.JSX.Element {
return (
<>
<div id="hero" data-section="hero">
<SectionErrorBoundary name="hero">
<HeroBillboard
title="La Basse Cour"
description="Cuisine traditionnelle française faite maison, avec des produits frais et de saison dans un cadre convivial et soigné."
primaryButton={{
text: "Réserver une table",
href: "#contact",
}}
secondaryButton={{
text: "Voir la carte",
href: "#features",
}}
imageSrc="http://img.b2bpic.net/free-photo/there-are-glasses-wine-water-table-with-white-cloth-are-ready-dining_613910-3428.jpg"
/>
</SectionErrorBoundary>
</div>
<>
<HeroSection />
<div id="about" data-section="about">
<SectionErrorBoundary name="about">
<AboutTextSplit
title="L'esprit de La Basse Cour"
descriptions={[
"Situé au 198 rue du Général Metman à Metz, notre établissement vous accueille dans une ambiance chaleureuse et rustique.",
"Nous mettons un point d'honneur à ne cuisiner que des produits frais, de saison, pour vous offrir une expérience culinaire maison authentique.",
"Profitez de notre salle récemment rénovée ou de notre terrasse cachée pour un moment de partage convivial.",
]}
/>
</SectionErrorBoundary>
</div>
<AboutSection />
<div id="features" data-section="features">
<SectionErrorBoundary name="features">
<FeaturesArrowCards
tag="Notre Carte"
title="Découvrez nos spécialités maison"
description="Une cuisine généreuse et traditionnelle avec une fourchette de prix entre 20 et 40 €."
items={[
{
title: "Parmentier de canard",
tags: [
"Plat Signature",
],
imageSrc: "http://img.b2bpic.net/free-photo/duck-breast-steak-white-plate_1203-8929.jpg",
},
{
title: "Rognons de veau",
tags: [
"Traditionnel",
],
imageSrc: "http://img.b2bpic.net/free-photo/mixed-salad-with-fried-eggplant-tomato-slices_141793-1216.jpg",
},
{
title: "Moules-frites",
tags: [
"Bistro",
],
imageSrc: "http://img.b2bpic.net/free-photo/grilled-salmon-fillet-with-lemon-slices-plate_84443-94426.jpg",
},
]}
/>
</SectionErrorBoundary>
</div>
<FeaturesSection />
<div id="pricing" data-section="pricing">
<SectionErrorBoundary name="pricing">
<PricingCenteredCards
tag="Infos Pratiques"
title="Nous trouver et nous visiter"
description="Ouvert tous les jours avec parking sur place et accès PMR."
plans={[
{
tag: "Ouverture",
price: "Quotidien",
description: "Nous vous accueillons du lundi au dimanche.",
features: [
"Ouvert midi et soir",
"Animaux acceptés",
"Cartes bancaires acceptées",
],
primaryButton: {
text: "Appeler",
href: "tel:0387753436",
},
},
{
tag: "Localisation",
price: "Metz",
description: "198 rue du Général Metman, 57070 Metz",
features: [
"Parking privé disponible",
"Accès facile PMR",
"Terrasse cachée",
],
primaryButton: {
text: "Itinéraire",
href: "https://maps.google.com/?q=198+rue+du+Général+Metman+57070+Metz",
},
},
]}
/>
</SectionErrorBoundary>
</div>
<PricingSection />
<div id="testimonials" data-section="testimonials">
<SectionErrorBoundary name="testimonials">
<TestimonialQuoteCards
tag="Nos clients"
title="Avis sur La Basse Cour"
description="Une note moyenne de 4,5/5 basée sur nos retours clients."
testimonials={[
{
name: "Marie D.",
role: "Habituée",
quote: "Plats généreux et accueil très souriant. Un vrai plaisir.",
imageSrc: "http://img.b2bpic.net/free-photo/portrait-beautiful-afro-american-woman_23-2148332136.jpg",
},
{
name: "Thomas P.",
role: "Fin Gourmet",
quote: "Cuisine maison délicieuse dans une ambiance chaleureuse.",
imageSrc: "http://img.b2bpic.net/free-photo/portrait-woman-smiling-kitchen_107420-12357.jpg",
},
{
name: "Sophie R.",
role: "Touriste",
quote: "Un super rapport qualité-prix à Metz, je recommande.",
imageSrc: "http://img.b2bpic.net/free-photo/pretty-woman-blue-clothes-smiling_23-2148055977.jpg",
},
{
name: "Julien M.",
role: "Local",
quote: "L'ambiance brasserie est top, très bonne équipe.",
imageSrc: "http://img.b2bpic.net/free-photo/beautiful-young-female-with-hair-bun-having-joyful-look-smiling-cheerfully-happy-with-some-positive-news_273609-9042.jpg",
},
{
name: "Claire L.",
role: "Cliente",
quote: "Le parmentier de canard est une merveille, bravo.",
imageSrc: "http://img.b2bpic.net/free-photo/close-up-people-celebrating-engagement_23-2149212184.jpg",
},
]}
/>
</SectionErrorBoundary>
</div>
<TestimonialsSection />
<div id="contact" data-section="contact">
<SectionErrorBoundary name="contact">
<ContactCta
tag="Réservation"
text="Réservez votre table par téléphone au 03 87 75 34 36 ou via notre formulaire de contact."
primaryButton={{
text: "Appeler maintenant",
href: "tel:0387753436",
}}
secondaryButton={{
text: "Itinéraire",
href: "https://maps.google.com/?q=198+rue+du+Général+Metman+57070+Metz",
}}
/>
</SectionErrorBoundary>
</div>
<ContactSection />
</>
);
}

View File

@@ -0,0 +1,23 @@
// AUTO-GENERATED by per-section-migrate. Edit freely — Bob will treat this
// file as the canonical source for the "about" section.
import React from 'react';
import AboutTextSplit from '@/components/sections/about/AboutTextSplit';
import SectionErrorBoundary from "@/components/ui/SectionErrorBoundary";
export default function AboutSection(): React.JSX.Element {
return (
<div id="about" data-section="about">
<SectionErrorBoundary name="about">
<AboutTextSplit
title="L'esprit de La Basse Cour"
descriptions={[
"Situé au 198 rue du Général Metman à Metz, notre établissement vous accueille dans une ambiance chaleureuse et rustique.",
"Nous mettons un point d'honneur à ne cuisiner que des produits frais, de saison, pour vous offrir une expérience culinaire maison authentique.",
"Profitez de notre salle récemment rénovée ou de notre terrasse cachée pour un moment de partage convivial.",
]}
/>
</SectionErrorBoundary>
</div>
);
}

View File

@@ -0,0 +1,27 @@
// AUTO-GENERATED by per-section-migrate. Edit freely — Bob will treat this
// file as the canonical source for the "contact" section.
import React from 'react';
import ContactCta from '@/components/sections/contact/ContactCta';
import SectionErrorBoundary from "@/components/ui/SectionErrorBoundary";
export default function ContactSection(): React.JSX.Element {
return (
<div id="contact" data-section="contact">
<SectionErrorBoundary name="contact">
<ContactCta
tag="Réservation"
text="Réservez votre table par téléphone au 03 87 75 34 36 ou via notre formulaire de contact."
primaryButton={{
text: "Appeler maintenant",
href: "tel:0387753436",
}}
secondaryButton={{
text: "Itinéraire",
href: "https://maps.google.com/?q=198+rue+du+Général+Metman+57070+Metz",
}}
/>
</SectionErrorBoundary>
</div>
);
}

View File

@@ -0,0 +1,43 @@
// AUTO-GENERATED by per-section-migrate. Edit freely — Bob will treat this
// file as the canonical source for the "features" section.
import React from 'react';
import FeaturesArrowCards from '@/components/sections/features/FeaturesArrowCards';
import SectionErrorBoundary from "@/components/ui/SectionErrorBoundary";
export default function FeaturesSection(): React.JSX.Element {
return (
<div id="features" data-section="features">
<SectionErrorBoundary name="features">
<FeaturesArrowCards
tag="Notre Carte"
title="Découvrez nos spécialités maison"
description="Une cuisine généreuse et traditionnelle avec une fourchette de prix entre 20 et 40 €."
items={[
{
title: "Parmentier de canard",
tags: [
"Plat Signature",
],
imageSrc: "http://img.b2bpic.net/free-photo/duck-breast-steak-white-plate_1203-8929.jpg",
},
{
title: "Rognons de veau",
tags: [
"Traditionnel",
],
imageSrc: "http://img.b2bpic.net/free-photo/mixed-salad-with-fried-eggplant-tomato-slices_141793-1216.jpg",
},
{
title: "Moules-frites",
tags: [
"Bistro",
],
imageSrc: "http://img.b2bpic.net/free-photo/grilled-salmon-fillet-with-lemon-slices-plate_84443-94426.jpg",
},
]}
/>
</SectionErrorBoundary>
</div>
);
}

View File

@@ -0,0 +1,28 @@
// AUTO-GENERATED by per-section-migrate. Edit freely — Bob will treat this
// file as the canonical source for the "hero" section.
import React from 'react';
import HeroBillboard from '@/components/sections/hero/HeroBillboard';
import SectionErrorBoundary from "@/components/ui/SectionErrorBoundary";
export default function HeroSection(): React.JSX.Element {
return (
<div id="hero" data-section="hero">
<SectionErrorBoundary name="hero">
<HeroBillboard
title="La Basse Cour"
description="Cuisine traditionnelle française faite maison, avec des produits frais et de saison dans un cadre convivial et soigné."
primaryButton={{
text: "Réserver une table",
href: "#contact",
}}
secondaryButton={{
text: "Voir la carte",
href: "#features",
}}
imageSrc="http://img.b2bpic.net/free-photo/there-are-glasses-wine-water-table-with-white-cloth-are-ready-dining_613910-3428.jpg"
/>
</SectionErrorBoundary>
</div>
);
}

View File

@@ -0,0 +1,50 @@
// AUTO-GENERATED by per-section-migrate. Edit freely — Bob will treat this
// file as the canonical source for the "pricing" section.
import React from 'react';
import PricingCenteredCards from '@/components/sections/pricing/PricingCenteredCards';
import SectionErrorBoundary from "@/components/ui/SectionErrorBoundary";
export default function PricingSection(): React.JSX.Element {
return (
<div id="pricing" data-section="pricing">
<SectionErrorBoundary name="pricing">
<PricingCenteredCards
tag="Infos Pratiques"
title="Nous trouver et nous visiter"
description="Ouvert tous les jours avec parking sur place et accès PMR."
plans={[
{
tag: "Ouverture",
price: "Quotidien",
description: "Nous vous accueillons du lundi au dimanche.",
features: [
"Ouvert midi et soir",
"Animaux acceptés",
"Cartes bancaires acceptées",
],
primaryButton: {
text: "Appeler",
href: "tel:0387753436",
},
},
{
tag: "Localisation",
price: "Metz",
description: "198 rue du Général Metman, 57070 Metz",
features: [
"Parking privé disponible",
"Accès facile PMR",
"Terrasse cachée",
],
primaryButton: {
text: "Itinéraire",
href: "https://maps.google.com/?q=198+rue+du+Général+Metman+57070+Metz",
},
},
]}
/>
</SectionErrorBoundary>
</div>
);
}

View File

@@ -0,0 +1,199 @@
/* eslint-disable */
// @ts-nocheck — generated by catalog-eject; runtime-correct but TS strict-mode false-positives on inlined catalog body
import Button from "@/components/ui/Button";
import TextAnimation from "@/components/ui/TextAnimation";
import ImageOrVideo from "@/components/ui/ImageOrVideo";
import GridOrCarousel from "@/components/ui/GridOrCarousel";
import ScrollReveal from "@/components/ui/ScrollReveal";
import Input from "@/components/ui/Input";
import Textarea from "@/components/ui/Textarea";
import Label from "@/components/ui/Label";
import { useState } from "react";
const testimonials = [
{
name: "Marie D.",
role: "Habituée",
quote: "Plats généreux et accueil très souriant. Un vrai plaisir.",
rating: 5
},
{
name: "Thomas P.",
role: "Fin Gourmet",
quote: "Cuisine maison délicieuse dans une ambiance chaleureuse.",
rating: 5
},
{
name: "Sophie R.",
role: "Touriste",
quote: "Un super rapport qualité-prix à Metz, je recommande.",
rating: 4
},
{
name: "Julien M.",
role: "Local",
quote: "L'ambiance brasserie est top, très bonne équipe.",
rating: 5
},
{
name: "Claire L.",
role: "Cliente",
quote: "Le parmentier de canard est une merveille, bravo.",
rating: 5
}
];
type Testimonial = {
name: string;
role: string;
quote: string;
rating: number;
};
const TestimonialsInline = () => {
const [reviews, setReviews] = useState(testimonials);
const [name, setName] = useState("");
const [quote, setQuote] = useState("");
const [rating, setRating] = useState(5);
const [submitted, setSubmitted] = useState(false);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (name && quote) {
setReviews([{ name, role: "Nouveau client", quote, rating }, ...reviews]);
setSubmitted(true);
setName("");
setQuote("");
setRating(5);
setTimeout(() => setSubmitted(false), 4000);
}
};
return (
<section aria-label="Testimonials section" className="py-24 bg-background-accent relative overflow-hidden">
<div className="absolute top-0 left-0 w-full h-full overflow-hidden pointer-events-none">
<div className="absolute -top-40 -right-40 w-96 h-96 bg-primary-cta opacity-5 rounded-full blur-3xl"></div>
<div className="absolute -bottom-40 -left-40 w-96 h-96 bg-card opacity-30 rounded-full blur-3xl"></div>
</div>
<div className="w-content-width mx-auto relative z-10">
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12 lg:gap-16 items-start">
<div className="lg:col-span-5 flex flex-col gap-8">
<div className="flex flex-col gap-4">
<div className="px-4 py-1.5 text-sm font-bold bg-primary-cta text-primary-cta-text rounded-full w-fit uppercase tracking-wider">
Votre avis compte
</div>
<TextAnimation
text="Partagez votre expérience"
variant="slide-up"
gradientText={false}
tag="h2"
className="text-5xl lg:text-6xl font-bold leading-tight text-primary-cta"
/>
<p className="text-lg text-primary-cta font-medium opacity-90">
Nous adorons avoir de vos nouvelles. Laissez-nous un petit mot sur votre repas à La Basse Cour !
</p>
</div>
<form onSubmit={handleSubmit} className="flex flex-col gap-5 bg-background p-8 rounded-3xl shadow-2xl border border-primary-cta/10">
{submitted ? (
<div className="flex flex-col items-center justify-center py-12 text-center gap-4 h-full min-h-[300px]">
<div className="w-20 h-20 bg-green-100 text-green-600 rounded-full flex items-center justify-center text-4xl mb-2 shadow-inner">
</div>
<h3 className="text-3xl font-bold text-foreground">Merci !</h3>
<p className="text-accent text-lg">Votre avis a bien é publié et ajouté à la liste.</p>
</div>
) : (
<>
<div className="flex flex-col gap-2">
<Label htmlFor="name" className="text-foreground font-bold text-sm uppercase tracking-wide">Votre nom</Label>
<Input
id="name"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Ex: Jean Dupont"
required={true}
className="bg-card border-none focus:ring-2 focus:ring-primary-cta rounded-xl py-3 px-4"
></Input>
</div>
<div className="flex flex-col gap-2">
<Label className="text-foreground font-bold text-sm uppercase tracking-wide">Votre note</Label>
<div className="flex gap-2 items-center bg-card w-fit px-4 py-2 rounded-xl">
{[1, 2, 3, 4, 5].map((star) => (
<button
key={star}
type="button"
onClick={() => setRating(star)}
className={`text-3xl transition-all duration-200 hover:scale-125 ${star <= rating ? 'text-yellow-500 drop-shadow-sm' : 'text-gray-300'}`}
>
</button>
))}
</div>
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="quote" className="text-foreground font-bold text-sm uppercase tracking-wide">Votre message</Label>
<Textarea
id="quote"
value={quote}
onChange={(e) => setQuote(e.target.value)}
placeholder="Racontez-nous votre moment..."
rows={4}
required={true}
className="bg-card border-none focus:ring-2 focus:ring-primary-cta resize-none rounded-xl py-3 px-4"
></Textarea>
</div>
<Button text="Publier mon avis" variant="primary" className="w-full mt-4 py-4 text-lg rounded-xl font-bold shadow-lg hover:shadow-xl transition-shadow" />
</>
)}
</form>
</div>
<div className="lg:col-span-7 flex flex-col gap-8">
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 bg-background/50 p-6 rounded-3xl backdrop-blur-sm border border-primary-cta/10">
<h3 className="text-3xl font-bold text-primary-cta">Derniers avis</h3>
<div className="flex items-center gap-3 bg-background px-5 py-3 rounded-2xl shadow-md">
<span className="font-black text-2xl text-foreground">4.5</span>
<span className="text-yellow-500 text-xl">½</span>
<span className="text-accent text-sm font-medium ml-1">({reviews.length} avis)</span>
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
{reviews.slice(0, 4).map((testimonial, idx) => (
<ScrollReveal key={idx} variant="slide-up" delay={idx * 0.1}>
<div className="flex flex-col gap-4 p-8 bg-card rounded-3xl shadow-lg border border-primary-cta/5 h-full hover:-translate-y-2 transition-transform duration-300 relative overflow-hidden group">
<div className="absolute top-0 right-0 w-24 h-24 bg-primary-cta opacity-5 rounded-bl-full -mr-4 -mt-4 transition-transform group-hover:scale-110"></div>
<div className="text-yellow-500 text-lg">
{Array.from({ length: 5 }).map((_, i) => (
<span key={i}>{i < testimonial.rating ? '★' : '☆'}</span>
))}
</div>
<p className="text-foreground text-lg leading-relaxed font-medium flex-grow relative z-10">"{testimonial.quote}"</p>
<div className="flex flex-col mt-4 pt-4 border-t border-primary-cta/10 relative z-10">
<span className="text-foreground font-bold text-xl">{testimonial.name}</span>
<span className="text-accent font-medium">{testimonial.role}</span>
</div>
</div>
</ScrollReveal>
))}
</div>
</div>
</div>
</div>
</section>
)};
export default function TestimonialsSection() {
return (
<div data-webild-section="testimonials" id="testimonials">
<TestimonialsInline />
</div>
);
}