```
✨ feat(ReserverController): Affiche les formules publiées et leur détail. ✨ feat(formule/show.twig): Crée template pour afficher détails d'une formule. 🎨 style(formules.twig): Améliore l'affichage des formules disponibles. ```
This commit is contained in:
@@ -145,17 +145,28 @@ class ReserverController extends AbstractController
|
||||
]);
|
||||
}
|
||||
#[Route('/reservation/formules', name: 'reservation_formules')]
|
||||
public function revervationFormules(): Response
|
||||
public function revervationFormules(FormulesRepository $formulesRepository): Response
|
||||
{
|
||||
|
||||
return $this->render('revervation/formules.twig',[
|
||||
'formules' => $formulesRepository->findBy(['isPublish'=>true],['updatedAt' => 'DESC']),
|
||||
]);
|
||||
}
|
||||
#[Route('/reservation/formules/{slug}', name: 'reservation_formule_show')]
|
||||
public function revervationView(): Response
|
||||
public function revervationView(string $slug,FormulesRepository $formulesRepository): Response
|
||||
{
|
||||
$parts = explode('-', $slug);
|
||||
$realId = $parts[0]; // Récupère le tout premier élément (l'index 0)
|
||||
|
||||
return $this->render('revervation/formules.twig',[
|
||||
// 2. Récupération du produit par son ID numérique
|
||||
$formule = $formulesRepository->find($realId);
|
||||
|
||||
if (!$formule) {
|
||||
throw $this->createNotFoundException('Formules introuvable');
|
||||
}
|
||||
|
||||
return $this->render('revervation/formule/show.twig',[
|
||||
'formule' => $formule
|
||||
]);
|
||||
}
|
||||
#[Route('/reservation/comment-reserver', name: 'reservation_workflow')]
|
||||
|
||||
128
templates/revervation/formule/show.twig
Normal file
128
templates/revervation/formule/show.twig
Normal file
@@ -0,0 +1,128 @@
|
||||
{% extends 'revervation/base.twig' %}
|
||||
|
||||
{# --- SEO --- #}
|
||||
{% block title %}{{ formule.name }} | Formule Événementielle - Ludik Event{% endblock %}
|
||||
{% block description %}{{ formule.description|striptags|slice(0, 160) }}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="min-h-screen bg-white font-sans antialiased pb-20">
|
||||
|
||||
{# --- HEADER / FIL D'ARIANE (STYLE SIMPLE) --- #}
|
||||
<div class="max-w-7xl mx-auto pt-16 pb-8 px-4 text-center">
|
||||
<nav class="flex justify-center space-x-4 text-[10px] mb-8 uppercase tracking-[0.3em] font-black italic">
|
||||
<a href="{{ url('reservation') }}" class="text-slate-400 hover:text-[#fc0e50] transition">ACCUEIL</a>
|
||||
<span class="text-slate-300">/</span>
|
||||
<a href="{{ url('reservation_formules') }}" class="text-slate-400 hover:text-[#fc0e50] transition">FORMULES</a>
|
||||
<span class="text-slate-300">/</span>
|
||||
<span class="text-[#f39e36] underline decoration-2 underline-offset-4">{{ formule.name }}</span>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{# --- SECTION HERO : VISUEL & PRIX --- #}
|
||||
<div class="max-w-7xl mx-auto px-4">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12 items-start">
|
||||
|
||||
{# Gauche : Image & Badge #}
|
||||
<div class="relative group">
|
||||
<div class="rounded-[3rem] overflow-hidden border-4 border-slate-900 shadow-[15px_15px_0px_0px_rgba(15,23,42,1)]">
|
||||
{% if formule.imageName %}
|
||||
<img src="{{ vich_uploader_asset(formule, 'imageFile') }}"
|
||||
alt="{{ formule.name }}"
|
||||
class="w-full h-auto object-contain transform group-hover:scale-105 transition-transform duration-700">
|
||||
{% else %}
|
||||
<div class="aspect-square bg-slate-100 flex items-center justify-center">
|
||||
<img src="{{ asset('provider/images/favicon.png') }}" class="w-32 opacity-20">
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{# Badge Type (Rose) #}
|
||||
<div class="absolute -top-4 -right-4 bg-[#fc0e50] text-white px-8 py-3 rounded-2xl border-4 border-slate-900 font-black text-sm uppercase italic rotate-3 shadow-lg">
|
||||
{{ formule.type|upper }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Droite : Infos & Réservation #}
|
||||
<div class="flex flex-col h-full">
|
||||
<h1 class="text-5xl md:text-7xl font-black text-slate-900 uppercase tracking-tighter italic leading-none mb-6">
|
||||
{{ formule.name|split(' ')|first }} <span class="text-[#f39e36]">{{ formule.name|split(' ')|slice(1)|join(' ') }}</span>
|
||||
</h1>
|
||||
|
||||
<div class="prose prose-slate mb-10">
|
||||
<div class="text-lg font-medium text-slate-600 leading-relaxed border-[#f39e36]">
|
||||
{% set desc = formule.description %}
|
||||
{% if '<p' in desc or '<div' in desc or '<span' in desc or '<br' in desc %}
|
||||
{{ desc|raw }}
|
||||
{% else %}
|
||||
{{ desc|nl2br }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# BOX TARIFS DÉGRESSIFS #}
|
||||
<div class="bg-slate-50 border-4 border-slate-900 rounded-[2.5rem] p-8 mb-8 relative">
|
||||
<div class="absolute -top-4 left-6 bg-white border-2 border-slate-900 px-4 py-1 rounded-full text-[10px] font-black uppercase text-[#f39e36]">Tarification Dégressive</div>
|
||||
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<div class="text-center">
|
||||
<p class="text-[10px] font-black text-slate-400 uppercase mb-1">1 JOUR</p>
|
||||
<p class="text-2xl font-black text-slate-900">{{ formule.price1j|format_currency('EUR') }}</p>
|
||||
</div>
|
||||
<div class="text-center border-x-2 border-slate-200">
|
||||
<p class="text-[10px] font-black text-slate-400 uppercase mb-1 text-[#f39e36]">2 JOURS</p>
|
||||
<p class="text-2xl font-black text-[#f39e36]">{{ formule.price2j|format_currency('EUR') }}</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="text-[10px] font-black text-slate-400 uppercase mb-1 text-[#fc0e50]">5 JOURS</p>
|
||||
<p class="text-2xl font-black text-[#fc0e50]">{{ formule.price5j|format_currency('EUR') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 pt-6 border-t border-slate-200 flex justify-between items-center">
|
||||
<span class="text-[10px] font-black text-slate-400 uppercase tracking-widest">Caution : {{ formule.caution|default('0') }}€</span>
|
||||
<span class="text-[10px] font-black text-[#fc0e50] uppercase tracking-widest italic animate-pulse">
|
||||
Économisez {{ (((formule.price1j * 5) - formule.price5j) / (formule.price1j * 5) * 100)|round }}% sur 5j
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# --- COMPOSITION DU PACK --- #}
|
||||
<div class="max-w-7xl mx-auto px-4 py-20">
|
||||
<div class="flex items-center space-x-4 mb-12">
|
||||
<h2 class="text-4xl font-black text-slate-900 uppercase italic tracking-tighter">Ce pack <span class="text-[#f39e36]">comprend</span></h2>
|
||||
<div class="h-1 flex-grow bg-slate-100 rounded-full"></div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
{# Liste des produits inclus #}
|
||||
<div class="space-y-4">
|
||||
<h3 class="text-xs font-black text-slate-400 uppercase tracking-[0.3em] mb-6">Équipements & Structures</h3>
|
||||
{% for item in formule.formulesProductIncluses %}
|
||||
<div class="flex items-center p-4 bg-white border-2 border-slate-100 rounded-3xl group hover:border-[#f39e36] transition-colors shadow-sm">
|
||||
<div class="w-16 h-16 rounded-2xl overflow-hidden border border-slate-100 flex-shrink-0 bg-slate-50">
|
||||
{% if item.product.imageName %}
|
||||
<img src="{{ vich_uploader_asset(item.product, 'imageFile') }}" class="w-full h-full object-cover">
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="ml-6 flex-grow">
|
||||
<p class="text-xs font-black text-[#f39e36] uppercase tracking-tighter">Réf: {{ item.product.ref }}</p>
|
||||
<h4 class="text-lg font-black text-slate-900 uppercase leading-none italic">{{ item.product.name }}</h4>
|
||||
</div>
|
||||
<div class="text-slate-300 group-hover:text-[#f39e36] transition-colors">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M5 13l4 4L19 7"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-slate-400 italic">Aucun produit inclus.</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{# BLOC INFOS COMPLEMENTAIRES #}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
{# --- SEO OPTIMISÉ --- #}
|
||||
{% block title %}Nos Formules Location | Packs Gonflables & Événements - Ludik Event{% endblock %}
|
||||
{% block description %}Économisez avec nos formules événementielles : packs châteaux gonflables, animations et gourmandises clés en main. Arrivée imminente de nos offres exclusives !{% endblock %}
|
||||
{% block description %}Découvrez nos packs événementiels clés en main. Location de châteaux gonflables, machines gourmandes et animations regroupés dans nos formules exclusives à prix dégressifs.{% endblock %}
|
||||
|
||||
{% block breadcrumb_json %}
|
||||
,{
|
||||
@@ -14,44 +14,97 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<utm-event event="view_formules_teasing"></utm-event>
|
||||
<utm-event event="view_formules_list"></utm-event>
|
||||
|
||||
<div class="min-h-screen bg-gray-50/50 font-sans antialiased pb-20">
|
||||
|
||||
{# --- HEADER --- #}
|
||||
<div class="max-w-7xl mx-auto pt-16 pb-8 px-4 text-center">
|
||||
<div class="max-w-7xl mx-auto pt-16 pb-12 px-4 text-center">
|
||||
<nav class="flex justify-center space-x-4 text-[10px] mb-8 uppercase tracking-[0.3em] font-black italic">
|
||||
<a href="{{ url('reservation') }}" class="text-slate-600 hover:text-[#fc0e50] transition">ACCUEIL</a>
|
||||
<span class="text-slate-500">/</span>
|
||||
<span class="text-amber-700 underline decoration-2 underline-offset-4">Nos Formules</span>
|
||||
<span class="text-amber-700 underline decoration-2 underline-offset-4 tracking-[0.3em]">NOS FORMULES</span>
|
||||
</nav>
|
||||
|
||||
<h1 class="text-5xl md:text-7xl font-black text-slate-900 uppercase tracking-tighter italic leading-none mb-6">
|
||||
Nos <span class="text-[#fc0e50]">Formules</span>
|
||||
<h1 class="text-6xl md:text-8xl font-black text-slate-900 uppercase tracking-tighter italic leading-none mb-6">
|
||||
PACKS <span class="text-[#fc0e50]">&</span> <span class="text-blue-600">FORMULES</span>
|
||||
</h1>
|
||||
<p class="max-w-2xl mx-auto text-slate-600 font-medium">
|
||||
Nous préparons des packs exclusifs pour simplifier l'organisation de vos fêtes. Restez connectés !
|
||||
<p class="max-w-2xl mx-auto text-slate-600 font-bold uppercase text-xs tracking-widest italic">
|
||||
Simplifiez votre événement avec nos solutions <span class="text-slate-900 underline decoration-[#f39e36] decoration-4">clés en main</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{# --- BENTO GRID : ARRIVÉE PROCHAINEMENT --- #}
|
||||
<div class="max-w-6xl mx-auto px-4 mt-12">
|
||||
<div class="md:col-span-2 md:row-span-2 bg-white border-2 border-slate-900 rounded-3xl p-8 flex flex-col justify-center relative overflow-hidden group shadow-[8px_8px_0px_0px_rgba(15,23,42,1)]">
|
||||
<div class="relative z-10">
|
||||
<span class="bg-amber-100 text-amber-700 text-xs font-bold px-3 py-1 rounded-full mb-4 inline-block">COMING SOON</span>
|
||||
<h2 class="text-4xl font-black text-slate-900 italic leading-tight uppercase">Bientôt disponible !</h2>
|
||||
<p class="mt-4 text-slate-500 font-medium">Nos experts concoctent des packs regroupant structures, alimentaire, etc pour des tarifs imbattables.</p>
|
||||
</div>
|
||||
{# Décoration de fond #}
|
||||
<div class="absolute -bottom-10 -right-10 text-9xl opacity-5 group-hover:rotate-12 transition-transform duration-500">🎈</div>
|
||||
</div>
|
||||
{# --- GRILLE DES FORMULES --- #}
|
||||
<div class="max-w-7xl mx-auto px-4 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-10">
|
||||
{% for formule in formules %}
|
||||
<div class="group bg-white rounded-[3rem] p-4 border-2 border-slate-900 shadow-[10px_10px_0px_0px_rgba(15,23,42,1)] hover:shadow-[15px_15px_0px_0px_rgba(15,23,42,1)] transition-all duration-300 flex flex-col">
|
||||
|
||||
{# --- BOUTON DE RETOUR --- #}
|
||||
<div class="mt-16 text-center">
|
||||
<p class="text-slate-400 text-sm mb-4">Besoin d'un devis sur mesure en attendant ?</p>
|
||||
<a href="{{ url('reservation_catalogue') }}" class="inline-block bg-white border-2 border-slate-900 px-8 py-4 rounded-full font-black uppercase italic tracking-wider hover:bg-slate-900 hover:text-white transition-all shadow-[4px_4px_0px_0px_rgba(15,23,42,1)] active:translate-y-1 active:shadow-none">
|
||||
Voir le catalogue
|
||||
</a>
|
||||
</div>
|
||||
{# Conteneur Image #}
|
||||
<div class="relative overflow-hidden rounded-[2.5rem] aspect-[4/5] bg-slate-100 flex items-center justify-center border-b-2 border-slate-900">
|
||||
{% if formule.imageName %}
|
||||
<img src="{{ vich_uploader_asset(formule, 'imageFile') | imagine_filter('product_card') }}"
|
||||
alt="{{ formule.name }}"
|
||||
loading="lazy"
|
||||
class="w-full h-full object-cover transform group-hover:scale-105 transition-transform duration-700">
|
||||
{% else %}
|
||||
<img src="{{ asset('provider/images/favicon.png') }}" class="w-32 h-32 opacity-20 grayscale">
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{# Infos & Prix #}
|
||||
<div class="mt-8 px-2 pb-4 flex-grow flex flex-col">
|
||||
<h3 class="text-3xl font-black text-slate-900 leading-none italic uppercase tracking-tighter group-hover:text-[#fc0e50] transition-colors">
|
||||
{{ formule.name }}
|
||||
</h3>
|
||||
|
||||
{# Tags / Avantages #}
|
||||
<div class="flex flex-wrap gap-2 mt-4">
|
||||
<span class="text-[9px] font-black bg-blue-50 text-blue-600 px-3 py-1 rounded-lg border border-blue-100">LIVRAISON POSSIBLE</span>
|
||||
<span class="text-[9px] font-black bg-amber-50 text-amber-600 px-3 py-1 rounded-lg border border-amber-100">TARIF DÉGRESSIF</span>
|
||||
</div>
|
||||
|
||||
{# Grille de Tarifs Dégressifs #}
|
||||
<div class="grid grid-cols-3 gap-3 mt-8 p-3 bg-slate-50 rounded-3xl border-2 border-slate-900/5">
|
||||
<div class="text-center">
|
||||
<p class="text-[8px] font-black text-slate-400 uppercase">1 JOUR</p>
|
||||
<p class="text-sm font-black text-slate-900">{{ formule.price1j|format_currency('EUR') }}</p>
|
||||
</div>
|
||||
<div class="text-center border-x border-slate-200">
|
||||
<p class="text-[8px] font-black text-slate-400 uppercase">2 JOURS</p>
|
||||
<p class="text-sm font-black text-blue-600">{{ formule.price2j|format_currency('EUR') }}</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="text-[8px] font-black text-slate-400 uppercase">5 JOURS</p>
|
||||
<p class="text-sm font-black text-emerald-600">{{ formule.price5j|format_currency('EUR') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-8">
|
||||
<a href="{{ path('reservation_formule_show', {slug: formule.slug|default(formule.id)}) }}"
|
||||
class="block w-full py-4 bg-[#f39e36] hover:bg-slate-900 text-white text-center rounded-2xl font-black uppercase italic tracking-widest transition-all border-2 border-slate-900 shadow-[4px_4px_0px_0px_rgba(15,23,42,1)] active:translate-y-1 active:shadow-none">
|
||||
Réserver ce pack
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="col-span-full py-32 text-center bg-white border-4 border-dashed border-slate-200 rounded-[4rem]">
|
||||
<div class="text-6xl mb-6">🎈</div>
|
||||
<h2 class="text-2xl font-black text-slate-900 uppercase italic">Mise à jour imminente</h2>
|
||||
<p class="text-slate-500 font-medium mt-2">Nos formules arrivent d'ici quelques minutes...</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{# --- CTA CATALOGUE --- #}
|
||||
<div class="mt-20 text-center">
|
||||
<p class="text-slate-500 font-bold uppercase text-[10px] tracking-[0.2em] mb-6">Besoin de composer votre propre bonheur ?</p>
|
||||
<a href="{{ url('reservation_catalogue') }}" class="inline-flex items-center space-x-3 bg-slate-900 text-white px-10 py-5 rounded-full font-black uppercase italic tracking-wider hover:bg-[#fc0e50] transition-all group">
|
||||
<span>Accéder au catalogue complet</span>
|
||||
<svg class="w-5 h-5 transform group-hover:translate-x-2 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M17 8l4 4m0 0l-4 4m4-4H3" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user