120 lines
5.0 KiB
TypeScript
120 lines
5.0 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useState } from 'react'
|
|
import Image from 'next/image'
|
|
import Link from 'next/link'
|
|
import { useRouter } from 'next/navigation'
|
|
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 router = useRouter()
|
|
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) }, [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: 'var(--color-background)' }}>
|
|
<AppBar title={tPlain(section.title, language)} onBack={() => router.back()} />
|
|
|
|
{/* Search */}
|
|
<div className="px-4 pt-3 pb-2">
|
|
<div
|
|
className="flex items-center gap-2 rounded-xl px-3 py-2"
|
|
style={{ background: 'var(--color-surface)', border: '1px solid var(--color-border)' }}
|
|
>
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" style={{ color: 'var(--color-text-muted)' }}>
|
|
<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: 'var(--color-text)' }}
|
|
/>
|
|
{search && (
|
|
<button onClick={() => setSearch('')} style={{ color: 'var(--color-text-muted)' }}>
|
|
<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="rounded-2xl overflow-hidden block relative"
|
|
style={{ background: 'var(--color-surface)', minHeight: 120 }}
|
|
>
|
|
{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: 'var(--color-text)' }}
|
|
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: 'var(--color-text-muted)' }}>
|
|
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" />
|
|
</svg>
|
|
</div>
|
|
)}
|
|
</Link>
|
|
))}
|
|
</main>
|
|
</div>
|
|
)
|
|
}
|