Compare commits
4 Commits
version_1_
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7e2804d2a4 | |||
|
|
8c38b23ebe | ||
| 009eeaf9eb | |||
| 585f1a9d96 |
@@ -1,177 +1,36 @@
|
||||
import AboutFeaturesSplit from '@/components/sections/about/AboutFeaturesSplit';
|
||||
import ContactCta from '@/components/sections/contact/ContactCta';
|
||||
import FaqSimple from '@/components/sections/faq/FaqSimple';
|
||||
import FeaturesMediaCards from '@/components/sections/features/FeaturesMediaCards';
|
||||
import HeroCenteredLogos from '@/components/sections/hero/HeroCenteredLogos';
|
||||
import MetricsSimpleCards from '@/components/sections/metrics/MetricsSimpleCards';
|
||||
import PricingHighlightedCards from '@/components/sections/pricing/PricingHighlightedCards';
|
||||
import TestimonialRatingCards from '@/components/sections/testimonial/TestimonialRatingCards';
|
||||
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 FeaturesSection from './HomePage/sections/Features';
|
||||
import ProcessSection from './HomePage/sections/Process';
|
||||
import MetricsSection from './HomePage/sections/Metrics';
|
||||
import PricingSection from './HomePage/sections/Pricing';
|
||||
import TestimonialsSection from './HomePage/sections/Testimonials';
|
||||
import FaqSection from './HomePage/sections/Faq';
|
||||
import ContactSection from './HomePage/sections/Contact';
|
||||
|
||||
export default function HomePage(): React.JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<div id="hero" data-section="hero">
|
||||
<SectionErrorBoundary name="hero">
|
||||
<HeroCenteredLogos
|
||||
avatarsSrc={[
|
||||
"http://img.b2bpic.net/free-photo/positive-confident-business-coach-posing-training-room_74855-3026.jpg", "http://img.b2bpic.net/free-photo/young-asian-waitress-cafe-manager-working-with-tablet-graphic-tablet_1258-199016.jpg", "http://img.b2bpic.net/black-businessman-happy-expression_1194-2623.jpg", "http://img.b2bpic.net/free-photo/portrait-african-american-person-smiling-working-his-living-room_482257-126267.jpg"]}
|
||||
avatarText="Rejoignez 500+ commerçants"
|
||||
title="La fidélité de demain, directement sur smartphone."
|
||||
description="Créez vos cartes virtuelles personnalisées en quelques clics. Zéro plastique, 100% engagement client."
|
||||
primaryButton={{
|
||||
text: "Pour les Commerçants", href: "#pricing"}}
|
||||
secondaryButton={{
|
||||
text: "Pour les Clients", href: "#features"}}
|
||||
names={[
|
||||
"Apple Pay", "Google Pay", "WhatsApp", "Instagram"]}
|
||||
imageSrc="http://img.b2bpic.net/free-vector/gradient-dark-mode-app-template_23-2150498015.jpg"
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
<>
|
||||
<HeroSection />
|
||||
|
||||
<div id="features" data-section="features">
|
||||
<SectionErrorBoundary name="features">
|
||||
<FeaturesMediaCards
|
||||
tag="Les avantages"
|
||||
title="Fidéliser n'a jamais été aussi simple."
|
||||
description="Une solution conçue pour maximiser le retour client tout en respectant l'environnement."
|
||||
items={[
|
||||
{
|
||||
title: "Éco-responsable", description: "Dites adieu aux cartes en papier jetables.", imageSrc: "http://img.b2bpic.net/free-photo/hand-hold-globe-paper-carving-with-grass-background_53876-14193.jpg"},
|
||||
{
|
||||
title: "Zéro oubli", description: "La carte est accessible via le wallet mobile du client.", imageSrc: "http://img.b2bpic.net/free-photo/cancel-sign-front-side-with-white-background_187299-39753.jpg"},
|
||||
{
|
||||
title: "Analytique avancée", description: "Suivez vos performances en temps réel.", imageSrc: "http://img.b2bpic.net/free-photo/golden-correct-sign-best-quality-assurance-guarantee-product-iso-service-concept_616485-97.jpg"},
|
||||
]}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
<FeaturesSection />
|
||||
|
||||
<div id="process" data-section="process">
|
||||
<SectionErrorBoundary name="process">
|
||||
<AboutFeaturesSplit
|
||||
tag="Fonctionnement"
|
||||
title="3 étapes vers la fidélité"
|
||||
description="Configurez, scannez, fidélisez. C'est tout."
|
||||
items={[
|
||||
{
|
||||
icon: "Settings", title: "Configurez", description: "Créez votre design personnalisé."},
|
||||
{
|
||||
icon: "QrCode", title: "Scannez", description: "QR Code unique pour chaque client."},
|
||||
{
|
||||
icon: "TrendingUp", title: "Fidélisez", description: "Récompensez vos clients fidèles."},
|
||||
]}
|
||||
imageSrc="http://img.b2bpic.net/free-photo/top-view-hands-scanning-qr-codes_23-2149357879.jpg"
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
<ProcessSection />
|
||||
|
||||
<div id="metrics" data-section="metrics">
|
||||
<SectionErrorBoundary name="metrics">
|
||||
<MetricsSimpleCards
|
||||
tag="Impact"
|
||||
title="Des chiffres qui comptent"
|
||||
description="Des résultats mesurables dès le premier mois."
|
||||
metrics={[
|
||||
{
|
||||
value: "+40%", description: "Taux de retour client"},
|
||||
{
|
||||
value: "10k+", description: "Cartes numériques émises"},
|
||||
{
|
||||
value: "2min", description: "Temps de mise en place"},
|
||||
]}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
<MetricsSection />
|
||||
|
||||
<div id="pricing" data-section="pricing">
|
||||
<SectionErrorBoundary name="pricing">
|
||||
<PricingHighlightedCards
|
||||
tag="Tarifs"
|
||||
title="Choisissez votre formule"
|
||||
description="Libre, Pro ou Entreprise, il y a un plan pour vous."
|
||||
plans={[
|
||||
{
|
||||
tag: "Découverte", price: "Gratuit", description: "Pour débuter", features: [
|
||||
"1 carte", "Support communauté", "Analyses basiques"],
|
||||
primaryButton: {
|
||||
text: "Commencer", href: "#"},
|
||||
},
|
||||
{
|
||||
tag: "Pro", price: "49€", description: "Pour les commerçants", features: [
|
||||
"Cartes illimitées", "Support prioritaire", "Push Notifications"],
|
||||
highlight: "Populaire", primaryButton: {
|
||||
text: "Essayer Pro", href: "#"},
|
||||
},
|
||||
{
|
||||
tag: "Enterprise", price: "199€", description: "Pour les réseaux", features: [
|
||||
"API dédiée", "Multi-sites", "Accompagnement"],
|
||||
primaryButton: {
|
||||
text: "Nous contacter", href: "#contact"},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
<PricingSection />
|
||||
|
||||
<div id="testimonials" data-section="testimonials">
|
||||
<SectionErrorBoundary name="testimonials">
|
||||
<TestimonialRatingCards
|
||||
tag="Témoignages"
|
||||
title="Ce que disent nos partenaires"
|
||||
description="Plus de 500 commerçants nous font confiance."
|
||||
testimonials={[
|
||||
{
|
||||
name: "Sophie Martin", role: "Boulangerie Artisanale", quote: "Un gain de temps incroyable et mes clients adorent le format numérique.", rating: 5,
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/studio-portrait-blond-female-dressed-white-shirt-red-eyeglasses_613910-14657.jpg"},
|
||||
{
|
||||
name: "Marc Dubois", role: "Coffee Shop", quote: "Le taux de retour est passé de 10% à 35% en deux mois !", rating: 5,
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/confident-asian-businesswoman-showing-thumbs-up-standing-near-entrance-her-cafe-restaurant_1258-199355.jpg"},
|
||||
{
|
||||
name: "Julie Leroy", role: "Concept Store", quote: "Facile à utiliser, design épuré, mes clients sont conquis.", rating: 5,
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/beautiful-woman-showing-her-fingers-gray-wall_114579-63656.jpg"},
|
||||
{
|
||||
name: "Antoine Petit", role: "Salon de Coiffure", quote: "La fin des cartes perdues au fond du portefeuille.", rating: 5,
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/young-man-wearing-blue-outfit-looking-crazy_1298-99.jpg"},
|
||||
{
|
||||
name: "Clara Vasseur", role: "Épicerie fine", quote: "Le meilleur outil de fidélisation du marché actuel.", rating: 5,
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/woman-smiling_1187-3196.jpg"},
|
||||
]}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
<TestimonialsSection />
|
||||
|
||||
<div id="faq" data-section="faq">
|
||||
<SectionErrorBoundary name="faq">
|
||||
<FaqSimple
|
||||
tag="Support"
|
||||
title="Questions fréquentes"
|
||||
description="Vous avez des doutes ? Nous avons les réponses."
|
||||
items={[
|
||||
{
|
||||
question: "Est-ce compatible avec tous les smartphones ?", answer: "Oui, compatible iOS et Android via les wallets natifs."},
|
||||
{
|
||||
question: "Comment mes clients utilisent-ils la carte ?", answer: "Simple scan QR à la caisse ou via NFC."},
|
||||
{
|
||||
question: "Y a-t-il un engagement ?", answer: "Non, vous pouvez résilier à tout moment."},
|
||||
]}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
<FaqSection />
|
||||
|
||||
<div id="contact" data-section="contact">
|
||||
<SectionErrorBoundary name="contact">
|
||||
<ContactCta
|
||||
tag="Lancez-vous"
|
||||
text="Prêt à digitaliser votre fidélité ? Contactez notre équipe dès aujourd'hui."
|
||||
primaryButton={{
|
||||
text: "Demander une démo", href: "#"}}
|
||||
secondaryButton={{
|
||||
text: "En savoir plus", href: "#"}}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
<ContactSection />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
23
src/pages/HomePage/sections/Contact.tsx
Normal file
23
src/pages/HomePage/sections/Contact.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
// 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="Lancez-vous"
|
||||
text="Prêt à digitaliser votre fidélité ? Contactez notre équipe dès aujourd'hui."
|
||||
primaryButton={{
|
||||
text: "Demander une démo", href: "#"}}
|
||||
secondaryButton={{
|
||||
text: "En savoir plus", href: "#"}}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
28
src/pages/HomePage/sections/Faq.tsx
Normal file
28
src/pages/HomePage/sections/Faq.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
// AUTO-GENERATED by per-section-migrate. Edit freely — Bob will treat this
|
||||
// file as the canonical source for the "faq" section.
|
||||
|
||||
import React from 'react';
|
||||
import FaqSimple from '@/components/sections/faq/FaqSimple';
|
||||
import SectionErrorBoundary from "@/components/ui/SectionErrorBoundary";
|
||||
|
||||
export default function FaqSection(): React.JSX.Element {
|
||||
return (
|
||||
<div id="faq" data-section="faq">
|
||||
<SectionErrorBoundary name="faq">
|
||||
<FaqSimple
|
||||
tag="Support"
|
||||
title="Questions fréquentes"
|
||||
description="Vous avez des doutes ? Nous avons les réponses."
|
||||
items={[
|
||||
{
|
||||
question: "Est-ce compatible avec tous les smartphones ?", answer: "Oui, compatible iOS et Android via les wallets natifs."},
|
||||
{
|
||||
question: "Comment mes clients utilisent-ils la carte ?", answer: "Simple scan QR à la caisse ou via NFC."},
|
||||
{
|
||||
question: "Y a-t-il un engagement ?", answer: "Non, vous pouvez résilier à tout moment."},
|
||||
]}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
101
src/pages/HomePage/sections/Features.tsx
Normal file
101
src/pages/HomePage/sections/Features.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
/* 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 { motion, useMotionValue, useTransform, useSpring } from 'motion/react';
|
||||
|
||||
const items = [
|
||||
{
|
||||
title: "Éco-responsable",
|
||||
description: "Dites adieu aux cartes en papier jetables.",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/hand-hold-globe-paper-carving-with-grass-background_53876-14193.jpg"
|
||||
},
|
||||
{
|
||||
title: "Zéro oubli",
|
||||
description: "La carte est accessible via le wallet mobile du client.",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/cancel-sign-front-side-with-white-background_187299-39753.jpg"
|
||||
},
|
||||
{
|
||||
title: "Analytique avancée",
|
||||
description: "Suivez vos performances en temps réel.",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/golden-correct-sign-best-quality-assurance-guarantee-product-iso-service-concept_616485-97.jpg"
|
||||
}
|
||||
];
|
||||
|
||||
type FeatureItem = {
|
||||
title: string;
|
||||
description: string;
|
||||
} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never });
|
||||
|
||||
interface FeaturesMediaCardsProps {
|
||||
tag: string;
|
||||
title: string;
|
||||
description: string;
|
||||
primaryButton?: { text: string; href: string };
|
||||
secondaryButton?: { text: string; href: string };
|
||||
items: FeatureItem[];
|
||||
}
|
||||
|
||||
const FeaturesInline = () => {
|
||||
return (
|
||||
<section aria-label="Features section" className="py-20">
|
||||
<div className="flex flex-col gap-8 md:gap-10">
|
||||
<div className="flex flex-col items-center w-content-width mx-auto gap-2">
|
||||
<div className="px-3 py-1 mb-1 text-sm card rounded w-fit">
|
||||
<p>{"Les avantages"}</p>
|
||||
</div>
|
||||
|
||||
<TextAnimation
|
||||
text={"Fidéliser n'a jamais été aussi simple."}
|
||||
variant="fade"
|
||||
gradientText={true}
|
||||
tag="h2"
|
||||
className="md:max-w-8/10 text-6xl 2xl:text-7xl leading-[1.15] font-semibold text-center text-balance"
|
||||
/>
|
||||
|
||||
<TextAnimation
|
||||
text={"Une solution conçue pour maximiser le retour client tout en respectant l'environnement."}
|
||||
variant="fade"
|
||||
gradientText={false}
|
||||
tag="p"
|
||||
className="md:max-w-7/10 text-lg md:text-xl leading-snug text-center text-balance"
|
||||
/>
|
||||
|
||||
{(undefined || undefined) && (
|
||||
<div className="flex flex-wrap justify-center gap-3 mt-2 md:mt-3">
|
||||
{undefined && <Button text={undefined.text} href={undefined.href} variant="primary"/>}
|
||||
{undefined && <Button text={undefined.text} href={undefined.href} variant="secondary"animationDelay={0.1} />}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ScrollReveal variant="fade">
|
||||
<GridOrCarousel >
|
||||
{items.map((item) => (
|
||||
<div key={item.title} className="flex flex-col gap-3 xl:gap-3.5 2xl:gap-4 p-3 xl:p-3.5 2xl:p-4 h-full card rounded">
|
||||
<div className="aspect-square rounded overflow-hidden button-secondary shadow shadow-foreground/5">
|
||||
<ImageOrVideo imageSrc={item.imageSrc} videoSrc={item.videoSrc} />
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 p-3 xl:p-3.5 2xl:p-4">
|
||||
<h3 className="text-2xl font-semibold leading-snug">{item.title}</h3>
|
||||
<p className="text-base leading-snug">{item.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</GridOrCarousel>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default function FeaturesSection() {
|
||||
return (
|
||||
<div data-webild-section="features" id="features">
|
||||
<FeaturesInline />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
99
src/pages/HomePage/sections/Hero.tsx
Normal file
99
src/pages/HomePage/sections/Hero.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
/* 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 HeroBackgroundSlot from "@/components/ui/HeroBackgroundSlot";
|
||||
import TextAnimation from "@/components/ui/TextAnimation";
|
||||
import ImageOrVideo from "@/components/ui/ImageOrVideo";
|
||||
import AvatarGroup from "@/components/ui/AvatarGroup";
|
||||
import { motion, useMotionValue, useTransform, useSpring } from 'motion/react';
|
||||
|
||||
const avatarsSrc = [
|
||||
"http://img.b2bpic.net/free-photo/positive-confident-business-coach-posing-training-room_74855-3026.jpg",
|
||||
"http://img.b2bpic.net/free-photo/young-asian-waitress-cafe-manager-working-with-tablet-graphic-tablet_1258-199016.jpg",
|
||||
"http://img.b2bpic.net/black-businessman-happy-expression_1194-2623.jpg",
|
||||
"http://img.b2bpic.net/free-photo/portrait-african-american-person-smiling-working-his-living-room_482257-126267.jpg"
|
||||
];
|
||||
const primaryButton = {
|
||||
text: "Pour les Commerçants",
|
||||
href: "#pricing"
|
||||
};
|
||||
const secondaryButton = {
|
||||
text: "Pour les Clients",
|
||||
href: "#features"
|
||||
};
|
||||
const names = [
|
||||
"Apple Pay",
|
||||
"Google Pay",
|
||||
"WhatsApp",
|
||||
"Instagram"
|
||||
];
|
||||
|
||||
type HeroCenteredLogosProps = {
|
||||
avatarsSrc: string[];
|
||||
avatarText: string;
|
||||
title: string;
|
||||
description: string;
|
||||
primaryButton: { text: string; href: string };
|
||||
secondaryButton: { text: string; href: string };
|
||||
names: string[];
|
||||
hideMedia?: boolean;
|
||||
} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never });
|
||||
|
||||
const HeroInline = () => {
|
||||
return (
|
||||
<section aria-label="Hero section" className="relative h-svh flex flex-col mb-20">
|
||||
<HeroBackgroundSlot />
|
||||
{!undefined && (
|
||||
<div className="absolute inset-0 z-0">
|
||||
<ImageOrVideo imageSrc={"http://img.b2bpic.net/free-vector/gradient-dark-mode-app-template_23-2150498015.jpg"} className="size-full object-cover" />
|
||||
<div className="absolute inset-0 bg-background/80" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="relative z-10 flex-1 flex items-center justify-center">
|
||||
<div className="flex flex-col items-center gap-3 pt-8 w-content-width mx-auto text-center">
|
||||
<AvatarGroup avatarsSrc={avatarsSrc} label={"Rejoignez 500+ commerçants"} size="lg" />
|
||||
|
||||
<TextAnimation
|
||||
text={"La fidélité de demain, directement sur smartphone."}
|
||||
variant="fade-blur"
|
||||
gradientText={true}
|
||||
tag="h1"
|
||||
className="md:max-w-8/10 text-7xl 2xl:text-8xl leading-[1.15] font-semibold text-center text-balance"
|
||||
/>
|
||||
|
||||
<TextAnimation
|
||||
text={"Créez vos cartes virtuelles personnalisées en quelques clics. Zéro plastique, 100% engagement client."}
|
||||
variant="fade-blur"
|
||||
gradientText={false}
|
||||
tag="p"
|
||||
className="md:max-w-7/10 text-lg md:text-xl leading-snug text-balance"
|
||||
/>
|
||||
|
||||
<div className="flex flex-wrap justify-center gap-3 mt-2 md:mt-3">
|
||||
<Button text={primaryButton.text} href={primaryButton.href} variant="primary" />
|
||||
<Button text={secondaryButton.text} href={secondaryButton.href} variant="secondary" animationDelay={0.1} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 w-content-width mx-auto pb-8 overflow-hidden mask-fade-x">
|
||||
<div className="flex w-max animate-marquee-horizontal" style={{ animationDuration: "30s" }}>
|
||||
{[...names, ...names, ...names, ...names].map((name, index) => (
|
||||
<div key={index} className="shrink-0 mx-3 px-4 py-2 card rounded">
|
||||
<span className="text-xl font-semibold whitespace-nowrap text-foreground/75">{name}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default function HeroSection() {
|
||||
return (
|
||||
<div data-webild-section="hero" id="hero">
|
||||
<HeroInline />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
28
src/pages/HomePage/sections/Metrics.tsx
Normal file
28
src/pages/HomePage/sections/Metrics.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
// AUTO-GENERATED by per-section-migrate. Edit freely — Bob will treat this
|
||||
// file as the canonical source for the "metrics" section.
|
||||
|
||||
import React from 'react';
|
||||
import MetricsSimpleCards from '@/components/sections/metrics/MetricsSimpleCards';
|
||||
import SectionErrorBoundary from "@/components/ui/SectionErrorBoundary";
|
||||
|
||||
export default function MetricsSection(): React.JSX.Element {
|
||||
return (
|
||||
<div id="metrics" data-section="metrics">
|
||||
<SectionErrorBoundary name="metrics">
|
||||
<MetricsSimpleCards
|
||||
tag="Impact"
|
||||
title="Des chiffres qui comptent"
|
||||
description="Des résultats mesurables dès le premier mois."
|
||||
metrics={[
|
||||
{
|
||||
value: "+40%", description: "Taux de retour client"},
|
||||
{
|
||||
value: "10k+", description: "Cartes numériques émises"},
|
||||
{
|
||||
value: "2min", description: "Temps de mise en place"},
|
||||
]}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
145
src/pages/HomePage/sections/Pricing.tsx
Normal file
145
src/pages/HomePage/sections/Pricing.tsx
Normal file
@@ -0,0 +1,145 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck — generated by catalog-eject; runtime-correct but TS strict-mode false-positives on inlined catalog body
|
||||
import { Check } from "lucide-react";
|
||||
import Button from "@/components/ui/Button";
|
||||
import TextAnimation from "@/components/ui/TextAnimation";
|
||||
import GridOrCarousel from "@/components/ui/GridOrCarousel";
|
||||
import ScrollReveal from "@/components/ui/ScrollReveal";
|
||||
import { cls } from "@/lib/utils";
|
||||
import { motion, useMotionValue, useTransform, useSpring } from 'motion/react';
|
||||
|
||||
const plans = [
|
||||
{
|
||||
tag: "Découverte",
|
||||
price: "Gratuit",
|
||||
description: "Pour débuter",
|
||||
features: [
|
||||
"1 carte",
|
||||
"Support communauté",
|
||||
"Analyses basiques"
|
||||
],
|
||||
primaryButton: {
|
||||
text: "Commencer",
|
||||
href: "#"
|
||||
}
|
||||
},
|
||||
{
|
||||
tag: "Pro",
|
||||
price: "49€",
|
||||
description: "Pour les commerçants",
|
||||
features: [
|
||||
"Cartes illimitées",
|
||||
"Support prioritaire",
|
||||
"Push Notifications"
|
||||
],
|
||||
highlight: "Populaire",
|
||||
primaryButton: {
|
||||
text: "Essayer Pro",
|
||||
href: "#"
|
||||
}
|
||||
},
|
||||
{
|
||||
tag: "Enterprise",
|
||||
price: "199€",
|
||||
description: "Pour les réseaux",
|
||||
features: [
|
||||
"API dédiée",
|
||||
"Multi-sites",
|
||||
"Accompagnement"
|
||||
],
|
||||
primaryButton: {
|
||||
text: "Nous contacter",
|
||||
href: "#contact"
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
type PricingPlan = {
|
||||
tag: string;
|
||||
price: string;
|
||||
description: string;
|
||||
features: string[];
|
||||
highlight?: string;
|
||||
primaryButton: { text: string; href: string };
|
||||
secondaryButton?: { text: string; href: string };
|
||||
};
|
||||
|
||||
const PricingInline = () => (
|
||||
<section aria-label="Pricing section" className="py-20">
|
||||
<div className="flex flex-col gap-8 md:gap-10">
|
||||
<div className="flex flex-col items-center w-content-width mx-auto gap-2">
|
||||
<div className="px-3 py-1 mb-1 text-sm card rounded w-fit">
|
||||
<p>{"Tarifs"}</p>
|
||||
</div>
|
||||
|
||||
<TextAnimation
|
||||
text={"Choisissez votre formule"}
|
||||
variant="fade-blur"
|
||||
gradientText={true}
|
||||
tag="h2"
|
||||
className="md:max-w-8/10 text-6xl 2xl:text-7xl leading-[1.15] font-semibold text-center text-balance"
|
||||
/>
|
||||
|
||||
<TextAnimation
|
||||
text={"Libre, Pro ou Entreprise, il y a un plan pour vous."}
|
||||
variant="fade-blur"
|
||||
gradientText={false}
|
||||
tag="p"
|
||||
className="md:max-w-7/10 text-lg md:text-xl leading-snug text-center text-balance"
|
||||
/>
|
||||
|
||||
{(undefined || undefined) && (
|
||||
<div className="flex flex-wrap justify-center gap-3 mt-2 md:mt-3">
|
||||
{undefined && <Button text={undefined.text} href={undefined.href} variant="primary"/>}
|
||||
{undefined && <Button text={undefined.text} href={undefined.href} variant="secondary"animationDelay={0.1} />}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ScrollReveal variant="fade-blur">
|
||||
<GridOrCarousel>
|
||||
{plans.map((plan) => (
|
||||
<div key={plan.tag} className="flex flex-col h-full">
|
||||
<div className={cls("px-5 py-2 text-base", plan.highlight ? "text-center primary-button rounded-t text-primary-cta-text" : "invisible")}>
|
||||
{plan.highlight || "placeholder"}
|
||||
</div>
|
||||
|
||||
<div className={cls("flex flex-col items-center gap-4 xl:gap-5 2xl:gap-6 p-6 xl:p-7 2xl:p-8 flex-1 card text-center", plan.highlight ? "rounded-t-none rounded-b" : "rounded")}>
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="text-5xl md:text-6xl font-semibold">{plan.price}</span>
|
||||
<span className="text-base font-medium">{plan.tag}</span>
|
||||
</div>
|
||||
|
||||
<div className="h-px w-full bg-foreground/20" />
|
||||
|
||||
<div className="flex flex-col gap-3 w-full">
|
||||
{plan.features.map((feature) => (
|
||||
<div key={feature} className="flex items-start gap-3">
|
||||
<div className="flex items-center justify-center shrink-0 size-6 primary-button rounded">
|
||||
<Check className="size-3 text-primary-cta-text" strokeWidth={2} />
|
||||
</div>
|
||||
<span className="text-base text-left">{feature}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-3 w-full mt-auto">
|
||||
<Button text={plan.primaryButton.text} href={plan.primaryButton.href} variant="primary" className="w-full" />
|
||||
{plan.secondaryButton && <Button text={plan.secondaryButton.text} href={plan.secondaryButton.href} variant="secondary" className="w-full" />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</GridOrCarousel>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
export default function PricingSection() {
|
||||
return (
|
||||
<div data-webild-section="pricing" id="pricing">
|
||||
<PricingInline />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
108
src/pages/HomePage/sections/Process.tsx
Normal file
108
src/pages/HomePage/sections/Process.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck — generated by catalog-eject; runtime-correct but TS strict-mode false-positives on inlined catalog body
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import Button from "@/components/ui/Button";
|
||||
import TextAnimation from "@/components/ui/TextAnimation";
|
||||
import ImageOrVideo from "@/components/ui/ImageOrVideo";
|
||||
import { resolveIcon } from "@/utils/resolve-icon";
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
const items = [
|
||||
{
|
||||
icon: "Settings",
|
||||
title: "Configurez",
|
||||
description: "Créez votre design personnalisé."
|
||||
},
|
||||
{
|
||||
icon: "QrCode",
|
||||
title: "Scannez",
|
||||
description: "QR Code unique pour chaque client."
|
||||
},
|
||||
{
|
||||
icon: "TrendingUp",
|
||||
title: "Fidélisez",
|
||||
description: "Récompensez vos clients fidèles."
|
||||
}
|
||||
];
|
||||
|
||||
type AboutFeaturesSplitProps = {
|
||||
tag: string;
|
||||
title: string;
|
||||
description: string;
|
||||
primaryButton?: { text: string; href: string };
|
||||
secondaryButton?: { text: string; href: string };
|
||||
items: { icon: string | LucideIcon; title: string; description: string }[];
|
||||
} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never });
|
||||
|
||||
const ProcessInline = () => {
|
||||
return (
|
||||
<section aria-label="About section" className="py-20">
|
||||
<div className="flex flex-col gap-8 md:gap-10 mx-auto w-content-width">
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<div className="px-3 py-1 mb-1 text-sm card rounded w-fit">
|
||||
<p>{"Fonctionnement"}</p>
|
||||
</div>
|
||||
|
||||
<TextAnimation
|
||||
text={"3 étapes vers la fidélité"}
|
||||
variant="fade-blur"
|
||||
gradientText={true}
|
||||
tag="h2"
|
||||
className="md:max-w-8/10 text-6xl 2xl:text-7xl leading-[1.15] font-semibold text-center text-balance"
|
||||
/>
|
||||
|
||||
<TextAnimation
|
||||
text={"Configurez, scannez, fidélisez. C'est tout."}
|
||||
variant="fade-blur"
|
||||
gradientText={false}
|
||||
tag="p"
|
||||
className="md:max-w-7/10 text-lg md:text-xl leading-snug text-center text-balance"
|
||||
/>
|
||||
|
||||
{(undefined || undefined) && (
|
||||
<div className="flex flex-wrap justify-center gap-3 mt-2 md:mt-3">
|
||||
{undefined && <Button text={undefined.text} href={undefined.href} variant="primary"/>}
|
||||
{undefined && <Button text={undefined.text} href={undefined.href} variant="secondary"animationDelay={0.1} />}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col md:flex-row md:items-stretch gap-5">
|
||||
<div className="flex flex-col justify-center gap-4 xl:gap-5 2xl:gap-6 p-6 xl:p-7 2xl:p-8 w-full md:w-4/10 2xl:w-35/100 card rounded">
|
||||
{items.map((item, index) => {
|
||||
const ItemIcon = resolveIcon(item.icon);
|
||||
return (
|
||||
<div key={item.title}>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center justify-center shrink-0 mb-1 size-10 primary-button rounded">
|
||||
<ItemIcon className="h-2/5 w-2/5 text-primary-cta-text" strokeWidth={1.5} />
|
||||
</div>
|
||||
<h3 className="text-2xl font-semibold">{item.title}</h3>
|
||||
<p className="text-base leading-snug">{item.description}</p>
|
||||
</div>
|
||||
{index < items.length - 1 && (
|
||||
<div className="mt-4 xl:mt-5 2xl:mt-6 border-b border-accent/40" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="p-px w-full md:w-6/10 2xl:w-7/10 h-80 md:h-auto card rounded overflow-hidden">
|
||||
<div className="relative size-full">
|
||||
<ImageOrVideo imageSrc={"http://img.b2bpic.net/free-photo/top-view-hands-scanning-qr-codes_23-2149357879.jpg"} className="absolute inset-0 object-cover rounded" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default function ProcessSection() {
|
||||
return (
|
||||
<div data-webild-section="process" id="process">
|
||||
<ProcessInline />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
37
src/pages/HomePage/sections/Testimonials.tsx
Normal file
37
src/pages/HomePage/sections/Testimonials.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
// AUTO-GENERATED by per-section-migrate. Edit freely — Bob will treat this
|
||||
// file as the canonical source for the "testimonials" section.
|
||||
|
||||
import React from 'react';
|
||||
import TestimonialRatingCards from '@/components/sections/testimonial/TestimonialRatingCards';
|
||||
import SectionErrorBoundary from "@/components/ui/SectionErrorBoundary";
|
||||
|
||||
export default function TestimonialsSection(): React.JSX.Element {
|
||||
return (
|
||||
<div id="testimonials" data-section="testimonials">
|
||||
<SectionErrorBoundary name="testimonials">
|
||||
<TestimonialRatingCards
|
||||
tag="Témoignages"
|
||||
title="Ce que disent nos partenaires"
|
||||
description="Plus de 500 commerçants nous font confiance."
|
||||
testimonials={[
|
||||
{
|
||||
name: "Sophie Martin", role: "Boulangerie Artisanale", quote: "Un gain de temps incroyable et mes clients adorent le format numérique.", rating: 5,
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/studio-portrait-blond-female-dressed-white-shirt-red-eyeglasses_613910-14657.jpg"},
|
||||
{
|
||||
name: "Marc Dubois", role: "Coffee Shop", quote: "Le taux de retour est passé de 10% à 35% en deux mois !", rating: 5,
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/confident-asian-businesswoman-showing-thumbs-up-standing-near-entrance-her-cafe-restaurant_1258-199355.jpg"},
|
||||
{
|
||||
name: "Julie Leroy", role: "Concept Store", quote: "Facile à utiliser, design épuré, mes clients sont conquis.", rating: 5,
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/beautiful-woman-showing-her-fingers-gray-wall_114579-63656.jpg"},
|
||||
{
|
||||
name: "Antoine Petit", role: "Salon de Coiffure", quote: "La fin des cartes perdues au fond du portefeuille.", rating: 5,
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/young-man-wearing-blue-outfit-looking-crazy_1298-99.jpg"},
|
||||
{
|
||||
name: "Clara Vasseur", role: "Épicerie fine", quote: "Le meilleur outil de fidélisation du marché actuel.", rating: 5,
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/woman-smiling_1187-3196.jpg"},
|
||||
]}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user