- Add responsive breakpoints (sm/md) to event_detail.html.twig: adaptive titles, stacked ticket layout on mobile, reduced padding/spacing - Add responsive breakpoints to order templates (guest, summary, public, payment, success): adaptive typography, padding, and layouts - Fix BreadcrumbList JSON-LD: escape names with json_encode, remove item URL from last breadcrumb - Update deploy.yml cron schedule from 3h/13h/19h/23h to 1h/22h - Add <title> tags to rgpd_deletion.html.twig and rgpd_access.html.twig - Add scope attributes to all <th> tags in rgpd_access.html.twig - Replace deprecated width/cellpadding/cellspacing HTML attributes with CSS in scan_force_notification email Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
270 lines
22 KiB
Twig
270 lines
22 KiB
Twig
{% extends 'base.html.twig' %}
|
|
|
|
{% block title %}{{ event.title }} - E-Ticket{% endblock %}
|
|
{% block description %}{{ event.title }} - {{ event.startAt|date('d/m/Y') }} a {{ event.city }}{% endblock %}
|
|
{% block og_type %}event{% endblock %}
|
|
{% block og_image %}
|
|
{% if event.eventMainPictureName %}
|
|
<meta property="og:image" content="{{ absolute_url('/uploads/events/' ~ event.eventMainPictureName) }}">
|
|
{% else %}
|
|
<meta property="og:image" content="https://ticket.e-cosplay.fr/logo.png">
|
|
{% endif %}
|
|
{% endblock %}
|
|
|
|
{% block body %}
|
|
<div class="bg-[#fbfbfb] overflow-x-hidden italic font-sans">
|
|
|
|
<section class="relative bg-white border-b-8 border-gray-900 px-4 pt-14 pb-10 sm:pt-20 sm:pb-16">
|
|
<div class="absolute inset-0 opacity-[0.03] pointer-events-none select-none overflow-hidden">
|
|
<span class="text-[5rem] sm:text-[8rem] md:text-[20rem] font-black uppercase leading-none block -rotate-12 translate-y-10">EVENT</span>
|
|
</div>
|
|
|
|
<div class="max-w-4xl mx-auto relative z-10">
|
|
<h1 class="text-3xl sm:text-4xl md:text-6xl font-black uppercase tracking-tighter leading-[0.85] mb-4 sm:mb-6">{{ event.title }}</h1>
|
|
|
|
<div class="flex flex-col sm:flex-row sm:flex-wrap gap-3 sm:gap-6 text-sm font-bold">
|
|
<div class="flex items-center gap-2">
|
|
<svg class="w-5 h-5 text-gray-400 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"/></svg>
|
|
<span>{{ event.address }}, {{ event.zipcode }} {{ event.city }}</span>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<svg class="w-5 h-5 text-gray-400 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/></svg>
|
|
<span>Du {{ event.startAt|date('d/m/Y H:i') }} au {{ event.endAt|date('d/m/Y H:i') }}</span>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<svg class="w-5 h-5 text-gray-400 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/></svg>
|
|
<a href="{{ path('app_organizer_detail', {id: organizer.id, slug: organizer.slug}) }}" class="text-indigo-800 underline hover:text-indigo-950">{{ organizer.companyName ?? (organizer.firstName ~ ' ' ~ organizer.lastName) }}</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="py-8 sm:py-12 px-4">
|
|
<div class="max-w-4xl mx-auto">
|
|
{% for message in app.flashes('success') %}
|
|
<div class="flash-success"><p class="font-black text-sm">{{ message }}</p></div>
|
|
{% endfor %}
|
|
{% for message in app.flashes('error') %}
|
|
<div class="flash-error"><p class="font-black text-sm">{{ message }}</p></div>
|
|
{% endfor %}
|
|
|
|
<div class="card-brutal overflow-hidden p-0">
|
|
<div class="flex flex-col md:flex-row">
|
|
<div class="md:w-[300px] flex-shrink-0">
|
|
{% if event.eventMainPictureName %}
|
|
<img src="{{ ('/uploads/events/' ~ event.eventMainPictureName) | imagine_filter('medium') }}" alt="{{ event.title }}" class="w-full max-h-[200px] sm:max-h-[300px] object-contain">
|
|
{% else %}
|
|
<div class="w-full h-[180px] sm:h-[250px] bg-gray-100 flex items-center justify-center">
|
|
<span class="text-5xl opacity-20">📷</span>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="p-4 sm:p-6 flex-1">
|
|
{% if event.description %}
|
|
<div class="prose prose-sm max-w-none font-bold text-gray-700 leading-relaxed">
|
|
{{ event.description|raw }}
|
|
</div>
|
|
{% else %}
|
|
<p class="text-gray-400 font-bold italic">Aucune description pour cet evenement.</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% set event_url = url('app_event_detail', {orgaSlug: organizer.slug, id: event.id, eventSlug: event.slug}) %}
|
|
<div class="flex flex-wrap items-center gap-3 mt-6">
|
|
<span class="text-[10px] font-black uppercase tracking-widest text-gray-400">Partager</span>
|
|
<a href="https://twitter.com/intent/tweet?text={{ (event.title ~ ' - ' ~ event.startAt|date('d/m/Y') ~ ' a ' ~ event.city)|url_encode }}&url={{ event_url|url_encode }}" target="_blank" rel="noopener" class="w-9 h-9 flex items-center justify-center border-2 border-gray-900 bg-black text-white hover:opacity-80 transition-all" aria-label="Partager sur X">
|
|
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
|
|
</a>
|
|
<a href="https://www.facebook.com/sharer/sharer.php?u={{ event_url|url_encode }}" target="_blank" rel="noopener" class="w-9 h-9 flex items-center justify-center border-2 border-gray-900 bg-[#1877F2] text-white hover:opacity-80 transition-all" aria-label="Partager sur Facebook">
|
|
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>
|
|
</a>
|
|
<button type="button" class="w-9 h-9 flex items-center justify-center border-2 border-gray-900 bg-gradient-to-br from-purple-600 via-pink-500 to-orange-400 text-white hover:opacity-80 transition-all cursor-pointer" aria-label="Copier le lien pour Instagram" data-share-copy="{{ event_url }}">
|
|
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>
|
|
</button>
|
|
<button type="button" class="w-9 h-9 flex items-center justify-center border-2 border-gray-900 bg-black text-white hover:opacity-80 transition-all cursor-pointer" aria-label="Copier le lien pour TikTok" data-share-copy="{{ event_url }}">
|
|
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z"/></svg>
|
|
</button>
|
|
<button type="button" class="w-9 h-9 flex items-center justify-center border-2 border-gray-900 bg-white hover:bg-gray-900 hover:text-white transition-all cursor-pointer" aria-label="Copier le lien" data-share-copy="{{ event_url }}">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"/></svg>
|
|
</button>
|
|
</div>
|
|
|
|
{% if categories|length > 0 %}
|
|
{% set event_ended = event.endAt and event.endAt < date() %}
|
|
<div class="card-brutal overflow-hidden p-0 mt-8" id="billetterie" data-stock-url="{{ path('app_event_stock', {id: event.id}) }}">
|
|
<div class="section-header">
|
|
<h2 class="text-sm font-black uppercase tracking-widest text-white">Billetterie</h2>
|
|
</div>
|
|
{% if event_ended %}
|
|
<div class="p-6">
|
|
<p class="font-black uppercase text-red-600 text-center">Cet evenement est termine. La billetterie est fermee.</p>
|
|
</div>
|
|
{% endif %}
|
|
{% if not event_ended %}
|
|
<div class="p-4 sm:p-6">
|
|
{% for category in categories %}
|
|
{% if category.active %}
|
|
{% set category_billets = billets[category.id] ?? [] %}
|
|
<div class="mb-6 {{ not loop.last ? 'border-b-2 border-gray-200 pb-6' : '' }}">
|
|
<h3 class="font-black uppercase text-sm tracking-widest mb-4">{{ category.name }}</h3>
|
|
|
|
{% if category_billets|length > 0 %}
|
|
<div class="space-y-3">
|
|
{% for billet in category_billets %}
|
|
<div class="border-2 border-gray-900 bg-white p-3 sm:p-4" data-cart-item data-billet-id="{{ billet.id }}" data-price="{{ billet.priceHTDecimal }}" data-max="{{ billet.quantity ?? 0 }}">
|
|
<div class="flex flex-col gap-3 sm:gap-4">
|
|
<div class="flex items-center gap-3 sm:gap-4 min-w-0">
|
|
{% if billet.pictureName %}
|
|
<img src="{{ ('/uploads/billets/' ~ billet.pictureName) | imagine_filter('thumbnail') }}" alt="{{ billet.name }}" class="w-12 h-12 sm:w-16 sm:h-16 object-cover border border-gray-300 flex-shrink-0">
|
|
{% endif %}
|
|
<div class="min-w-0 flex-1">
|
|
<p class="font-black uppercase text-xs sm:text-sm">{{ billet.name }}</p>
|
|
{% if billet.description %}
|
|
<p class="text-xs text-gray-500 font-bold mt-1 line-clamp-2">{{ billet.description }}</p>
|
|
{% endif %}
|
|
<p class="text-[10px] font-bold mt-1" data-stock-label>
|
|
{% if not billet.unlimited and billet.quantity is not null %}
|
|
{% if billet.quantity == 0 %}
|
|
<span class="text-red-600">Rupture de stock</span>
|
|
{% elseif billet.quantity <= 10 %}
|
|
<span class="text-orange-500">Plus que {{ billet.quantity }} place{{ billet.quantity > 1 ? 's' : '' }} !</span>
|
|
{% else %}
|
|
<span class="text-gray-400">{{ billet.quantity }} place{{ billet.quantity > 1 ? 's' : '' }} disponible{{ billet.quantity > 1 ? 's' : '' }}</span>
|
|
{% endif %}
|
|
{% endif %}
|
|
</p>
|
|
</div>
|
|
<p class="font-black text-indigo-600 text-base sm:text-lg flex-shrink-0 md:hidden">{{ billet.priceHTDecimal|number_format(2, ',', ' ') }} €</p>
|
|
</div>
|
|
|
|
<div class="flex items-center justify-between gap-3 sm:gap-4">
|
|
<p class="font-black text-indigo-600 text-lg w-24 text-right hidden md:block flex-shrink-0">{{ billet.priceHTDecimal|number_format(2, ',', ' ') }} €</p>
|
|
|
|
<div class="flex items-center border-2 border-gray-900 flex-shrink-0">
|
|
<button type="button" data-cart-minus aria-label="Retirer un {{ billet.name }}" class="w-8 h-8 sm:w-9 sm:h-9 flex items-center justify-center font-black text-lg hover:bg-gray-100 transition-all cursor-pointer select-none">-</button>
|
|
<input type="number" data-cart-qty min="0" max="{{ billet.quantity ?? 99 }}" value="0" aria-label="Quantite {{ billet.name }}" class="w-10 h-8 sm:w-12 sm:h-9 text-center font-black text-sm border-x-2 border-gray-900 outline-none" readonly>
|
|
<button type="button" data-cart-plus aria-label="Ajouter un {{ billet.name }}" class="w-8 h-8 sm:w-9 sm:h-9 flex items-center justify-center font-black text-lg hover:bg-gray-100 transition-all cursor-pointer select-none">+</button>
|
|
</div>
|
|
|
|
<p class="font-black text-sm w-20 sm:w-24 text-right flex-shrink-0" data-cart-line-total>0,00 €</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<p class="text-gray-400 font-bold text-sm">Aucun billet disponible dans cette categorie.</p>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
<div class="border-t-3 border-gray-900 pt-4 sm:pt-6 mt-4 sm:mt-6">
|
|
<div class="flex items-center justify-between mb-3 sm:mb-4">
|
|
<span class="font-black uppercase text-xs sm:text-sm tracking-widest">Total</span>
|
|
<span class="font-black text-xl sm:text-2xl text-indigo-600" id="cart-total">0,00 €</span>
|
|
</div>
|
|
<div class="flex items-center justify-between mb-4 sm:mb-6">
|
|
<span class="text-[10px] sm:text-xs font-bold text-gray-400 uppercase tracking-widest">Articles</span>
|
|
<span class="font-black text-sm" id="cart-count">0</span>
|
|
</div>
|
|
<div id="cart-error" class="hidden flash-error mb-4">
|
|
<p class="font-black text-sm" id="cart-error-text"></p>
|
|
</div>
|
|
<button type="button" id="cart-checkout" disabled data-order-url="{{ path('app_order_create', {id: event.id}) }}" class="w-full btn-brutal font-black uppercase text-xs sm:text-sm tracking-widest bg-indigo-600 text-white hover:bg-indigo-800 transition-all disabled:opacity-30 disabled:cursor-not-allowed">
|
|
Commander
|
|
</button>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="card-brutal overflow-hidden p-0 mt-8">
|
|
<div class="section-header">
|
|
<h2 class="text-sm font-black uppercase tracking-widest text-white">Emplacement</h2>
|
|
</div>
|
|
<div id="event-map" class="w-full h-[200px] sm:h-[300px]" data-address="{{ event.address }}, {{ event.zipcode }} {{ event.city }}"></div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="py-8 sm:py-12 px-4">
|
|
<div class="max-w-4xl mx-auto flex flex-col lg:flex-row gap-6 sm:gap-8">
|
|
|
|
<div class="flex-1">
|
|
<div class="card-brutal overflow-hidden">
|
|
<div class="section-header">
|
|
<h2 class="text-sm font-black uppercase tracking-widest text-white">Organisateur</h2>
|
|
</div>
|
|
<div class="p-4 sm:p-6">
|
|
<div class="flex items-center gap-3 sm:gap-4 mb-4 sm:mb-6">
|
|
{% if organizer.logoName %}
|
|
<div class="border-3 border-gray-900 overflow-hidden bg-white p-1 shadow-[4px_4px_0px_rgba(0,0,0,1)]">
|
|
<img src="{{ ('/uploads/logos/' ~ organizer.logoName) | imagine_filter('thumbnail') }}" alt="{{ organizer.companyName }}" class="h-[60px] w-auto object-contain">
|
|
</div>
|
|
{% else %}
|
|
<div class="w-16 h-16 bg-yellow-400 border-3 border-gray-900 flex items-center justify-center shadow-[4px_4px_0px_rgba(0,0,0,1)]">
|
|
<span class="text-2xl font-black">{{ organizer.firstName|first|upper }}{{ organizer.lastName|first|upper }}</span>
|
|
</div>
|
|
{% endif %}
|
|
<div>
|
|
<a href="{{ path('app_organizer_detail', {id: organizer.id, slug: organizer.slug}) }}" class="font-black text-lg uppercase tracking-tighter hover:text-indigo-600 transition-colors">{{ organizer.companyName ?? (organizer.firstName ~ ' ' ~ organizer.lastName) }}</a>
|
|
{% if organizer.city %}
|
|
<p class="text-xs font-bold text-gray-400 uppercase tracking-widest">{{ organizer.postalCode }} {{ organizer.city }}</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
{% include 'components/_social_icons.html.twig' with {organizer: organizer, class: 'flex-wrap mb-4'} only %}
|
|
|
|
{% if organizer.email %}
|
|
<p class="text-sm font-bold"><a href="mailto:{{ organizer.email }}" class="text-indigo-800 underline hover:text-indigo-950">{{ organizer.email }}</a></p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex-1">
|
|
<div class="card-brutal overflow-hidden">
|
|
<div class="section-header">
|
|
<h2 class="text-sm font-black uppercase tracking-widest text-white">Contacter l'organisateur</h2>
|
|
</div>
|
|
<div class="p-4 sm:p-6">
|
|
<form method="post" action="{{ path('app_event_contact', {id: event.id}) }}" class="form-col">
|
|
<div class="form-row">
|
|
<div class="form-group">
|
|
<label for="contact_name" class="text-xs font-black uppercase tracking-widest form-label">Nom</label>
|
|
<input type="text" id="contact_name" name="name" required class="form-input focus:border-indigo-600" placeholder="Dupont">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="contact_firstname" class="text-xs font-black uppercase tracking-widest form-label">Prenom</label>
|
|
<input type="text" id="contact_firstname" name="firstname" required class="form-input focus:border-indigo-600" placeholder="Jean">
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<label for="contact_email" class="text-xs font-black uppercase tracking-widest form-label">Email</label>
|
|
<input type="email" id="contact_email" name="email" required class="form-input focus:border-indigo-600" placeholder="jean.dupont@exemple.fr">
|
|
</div>
|
|
<div>
|
|
<label for="contact_message" class="text-xs font-black uppercase tracking-widest form-label">Message</label>
|
|
<textarea id="contact_message" name="message" required rows="4" class="form-textarea focus:border-indigo-600" placeholder="Votre question sur cet evenement..."></textarea>
|
|
</div>
|
|
<div>
|
|
<button type="submit" class="btn-brutal font-black uppercase text-sm tracking-widest hover:bg-indigo-600 hover:text-white transition-all">
|
|
Envoyer
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</section>
|
|
|
|
</div>
|
|
{% endblock %}
|