Import initial de la documentation : statut, roadmap, plans V1/V2, specs verticales (creche, sport), audits securite, plan de test, analyse concurrentielle et maquettes de design. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
123 lines
5.0 KiB
TypeScript
123 lines
5.0 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useState } from 'react'
|
|
import Image from 'next/image'
|
|
import Link from 'next/link'
|
|
import { useBack } from '@/hooks/useBack'
|
|
import { useVisitor } from '@/context/VisitorContext'
|
|
import { t, tPlain } from '@/lib/i18n'
|
|
import type { SectionDTO } from '@/lib/api/types'
|
|
import AppBar from '@/components/ui/AppBar'
|
|
import { trackEvent } from '@/lib/stats'
|
|
|
|
interface Props {
|
|
section: SectionDTO
|
|
slug: string
|
|
configId: string
|
|
languages: string[]
|
|
}
|
|
|
|
export default function MenuSection({ section, slug, configId, languages }: Props) {
|
|
const { language, setAvailableLanguages, instanceId } = useVisitor()
|
|
const back = useBack()
|
|
const [search, setSearch] = useState('')
|
|
|
|
function handleItemClick(sub: SectionDTO) {
|
|
if (!instanceId) return
|
|
trackEvent({
|
|
instanceId, configurationId: configId, sectionId: section.id,
|
|
eventType: 'MenuItemTap', language,
|
|
metadata: JSON.stringify({ targetSectionId: sub.id, title: tPlain(sub.title, language) }),
|
|
})
|
|
}
|
|
|
|
useEffect(() => { setAvailableLanguages([]) }, [languages])
|
|
|
|
const subsections = [...(section.menu?.sections ?? [])]
|
|
.filter((s) => s.isActive !== false)
|
|
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
|
|
|
|
const filtered = subsections.filter((s) => {
|
|
if (!search) return true
|
|
const title = tPlain(s.title, language).toLowerCase()
|
|
return title.includes(search.toLowerCase())
|
|
})
|
|
|
|
return (
|
|
<div className="min-h-screen flex flex-col" style={{ background: '#F6F3EE' }}>
|
|
<AppBar title={tPlain(section.title, language)} onBack={back} />
|
|
|
|
{/* Search */}
|
|
<div className="px-4 pt-3 pb-2">
|
|
<div
|
|
className="flex items-center gap-2 px-3 py-2.5"
|
|
style={{ background: '#F6F3EE', border: '1px solid #E2DCD2', borderRadius: 14 }}
|
|
>
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" style={{ color: '#6B7B86' }}>
|
|
<path d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
|
|
</svg>
|
|
<input
|
|
type="text"
|
|
value={search}
|
|
onChange={(e) => setSearch(e.target.value)}
|
|
placeholder="Rechercher..."
|
|
className="flex-1 bg-transparent text-sm outline-none"
|
|
style={{ color: '#1E2A33' }}
|
|
/>
|
|
{search && (
|
|
<button onClick={() => setSearch('')} style={{ color: '#6B7B86' }}>
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Grid */}
|
|
<main className="flex-1 overflow-y-auto p-4 grid grid-cols-2 gap-3">
|
|
{filtered.length === 0 && (
|
|
<p className="col-span-2 text-center py-12 text-sm" style={{ color: 'var(--color-text-muted)' }}>
|
|
Aucun résultat
|
|
</p>
|
|
)}
|
|
{filtered.map((sub) => (
|
|
<Link
|
|
key={sub.id}
|
|
href={`/${slug}/${configId}/sections/${sub.id}`}
|
|
onClick={() => handleItemClick(sub)}
|
|
className="overflow-hidden block relative"
|
|
style={{
|
|
background: '#fff', minHeight: 120, borderRadius: 20,
|
|
border: '1px solid #EFEAE2', boxShadow: '0 6px 16px -12px rgba(0,0,0,0.3)',
|
|
}}
|
|
>
|
|
{sub.imageSource ? (
|
|
<div className="relative w-full h-32">
|
|
<Image src={sub.imageSource} alt={tPlain(sub.title, language)} fill className="object-cover" sizes="50vw" />
|
|
<div className="absolute inset-0" style={{ background: 'linear-gradient(to top, rgba(0,0,0,0.6) 0%, transparent 60%)' }} />
|
|
<div
|
|
className="absolute bottom-2 left-3 right-7 text-white text-sm font-semibold leading-tight [&_p]:m-0 [&_p]:leading-tight"
|
|
dangerouslySetInnerHTML={{ __html: t(sub.title, language) }}
|
|
/>
|
|
<svg className="absolute bottom-2 right-2" width="15" height="15" viewBox="0 0 24 24" fill="white">
|
|
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" />
|
|
</svg>
|
|
</div>
|
|
) : (
|
|
<div className="p-4 relative" style={{ minHeight: 80 }}>
|
|
<div
|
|
className="text-sm font-semibold pr-6 [&_p]:m-0"
|
|
style={{ color: '#1E2A33' }}
|
|
dangerouslySetInnerHTML={{ __html: t(sub.title, language) }}
|
|
/>
|
|
<svg className="absolute bottom-2 right-2" width="15" height="15" viewBox="0 0 24 24" fill="currentColor" style={{ color: '#6B7B86' }}>
|
|
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" />
|
|
</svg>
|
|
</div>
|
|
)}
|
|
</Link>
|
|
))}
|
|
</main>
|
|
</div>
|
|
)
|
|
}
|