'use client'; import { useEffect, useRef, useState } from 'react'; import { useTranslations } from 'next-intl'; import { ProjectCard } from '@/components/ui/ProjectCard'; import { ContactFormModal } from '@/components/ui/ContactFormModal'; import { Project } from '@/lib/projects/types'; type props = { projects: Project[]; locale: string; }; export default function FreelanceClient({ projects, locale }: props) { const t = useTranslations('freelancePage'); const [contactOpen, setContactOpen] = useState(false); const heroRef = useRef(null); const belowFoldRef = useRef(null); useEffect(() => { const section = heroRef.current; if (!section) return; const items = section.querySelectorAll('.hero-reveal'); items.forEach((el, i) => { setTimeout(() => { el.style.opacity = '1'; el.style.transform = 'translateY(0)'; }, 120 + i * 110); }); }, []); useEffect(() => { const container = belowFoldRef.current; if (!container) return; const sections = container.querySelectorAll('[data-reveal-section]'); const observers: IntersectionObserver[] = []; sections.forEach((section) => { const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { entry.target.querySelectorAll('.reveal').forEach((el, i) => { setTimeout(() => el.classList.add('visible'), i * 100); }); observer.unobserve(entry.target); } }); }, { threshold: 0.1 } ); observer.observe(section); observers.push(observer); }); return () => observers.forEach((o) => o.disconnect()); }, []); const needs = [ { title: t('needs.item1Title'), text: t('needs.item1Text') }, { title: t('needs.item2Title'), text: t('needs.item2Text') }, { title: t('needs.item3Title'), text: t('needs.item3Text') }, { title: t('needs.item4Title'), text: t('needs.item4Text') }, ]; const services = [ { title: t('services.service1Title'), desc: t('services.service1Desc'), ideal: t('services.service1Ideal'), examples: t('services.service1Examples').split(' · '), }, { title: t('services.service2Title'), desc: t('services.service2Desc'), ideal: t('services.service2Ideal'), examples: t('services.service2Examples').split(' · '), }, { title: t('services.service3Title'), desc: t('services.service3Desc'), ideal: t('services.service3Ideal'), examples: t('services.service3Examples').split(' · '), }, ]; const methodSteps = Array.from({ length: 5 }, (_, i) => ({ title: t(`method.step${i + 1}Title`), text: t(`method.step${i + 1}Text`), })); const whyArgs = Array.from({ length: 4 }, (_, i) => ({ title: t(`why.arg${i + 1}Title`), text: t(`why.arg${i + 1}Text`), })); const heroCards = [ { num: '01', label: t('hero.card1Label') }, { num: '02', label: t('hero.card2Label') }, { num: '03', label: t('hero.card3Label') }, ]; return (
setContactOpen(false)} /> {/* ── Hero ── */}
{/* Left — text */}
{t('hero.label')}

{t('hero.title')}

{t('hero.subtitle')}

{/* Right — service list, editorial style */}
{heroCards.map((card, i) => (
{card.num} {card.label}
))}
{/* ── Below fold ── */}
{/* ── Needs ── */}
{t('needs.sectionLabel')}
{/* Grid "mortier" — les lignes de séparation sont le fond du container */}
{needs.map((item, i) => (
{String(i + 1).padStart(2, '0')}

{item.title}

{item.text}

))}
{/* ── Services — catalogue ── */}
{t('services.sectionLabel')}
{services.map((service, i) => (
{/* En-tête catalogue */}
{String(i + 1).padStart(2, '0')} {t('services.sectionLabel')}
{/* Corps */}

{service.title}

{service.desc}

{/* Pied catalogue */}
{t('services.idealLabel')}

{service.ideal}

{service.examples.map((ex, j) => ( {ex} ))}
))}
{/* ── Method — liste structurée ── */}
{t('method.sectionLabel')}
{methodSteps.map((step, i) => (
{String(i + 1).padStart(2, '0')}

{step.title}

{step.text}

))}
{/* ── Works ── */}
{t('works.sectionLabel')}
{projects.map((project, i) => ( ))}
{/* ── Why ── */}
{t('why.sectionLabel')}

{t('why.intro')}

{/* Grille "mortier" identique à Needs */}
{whyArgs.map((arg, i) => (

{arg.title}

{arg.text}

))}
{/* ── CTA final ── */}

{t('cta.title')}

{t('cta.text')}

{t('cta.note1')} {t('cta.note2')}
); }