Thomas Fransolet a5a8ecdb20 Documentation interne MyInfoMate / Unov
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>
2026-08-11 11:17:01 +02:00

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>
)
}