- Create Billet entity: name, position, priceHT, quantity (nullable=unlimited), isGeneratedBillet, hasDefinedExit, notBuyable, type (billet/reservation_brocante/vote), stripeProductId, description, picture (VichUploader), category (ManyToOne CASCADE) - Create BilletDesign entity (OneToOne Event): accentColor, invitationTitle, invitationColor - Billet CRUD: add/edit/delete with access control, Stripe product sync on connected account - Billet reorder: drag & drop with position field, refactored sortable.js for both categories and billets - Ticket designer tab (custom offer only): accent color, invitation title/color, live iframe preview - A4 ticket preview: 4 zones (HG infos+billet, HD affiche, BG association, BD sortie+invitation), fake QR code SVG - Commission calculator JS: live breakdown of E-Ticket fee, Stripe fee (1.5%+0.25EUR), net amount - Sales recap on categories tab: qty sold, total HT, total commissions, total net - DisableProfilerSubscriber: disable web profiler toolbar on preview iframe - CSP: allow self in frame-src and frame-ancestors for preview iframe - Flysystem: dedicated billets.storage for billet images - Upload accept restricted to png/jpeg/webp/gif (no HEIC) - Makefile: add force_sql_dev command - CLAUDE.md: add rule to never modify existing migrations - Consolidate all migrations into single Version20260321111125 - Tests: BilletTest (20), BilletDesignTest (6), DisableProfilerSubscriberTest (5), billet-designer.test.js (7), commission-calculator.test.js (7), AccountControllerTest billet CRUD tests (11) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
81 lines
4.7 KiB
Twig
81 lines
4.7 KiB
Twig
{% extends 'base.html.twig' %}
|
|
|
|
{% block title %}Ajouter un billet - {{ category.name }} - E-Ticket{% endblock %}
|
|
|
|
{% block body %}
|
|
<div class="page-container">
|
|
<a href="{{ path('app_account_edit_event', {id: event.id, tab: 'categories'}) }}" class="inline-flex items-center gap-2 text-sm font-black uppercase tracking-widest text-gray-500 hover:text-gray-900 transition-colors mb-8">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M15 19l-7-7 7-7"/></svg>
|
|
Retour aux categories
|
|
</a>
|
|
|
|
<h1 class="text-3xl font-black uppercase tracking-tighter italic heading-page">Ajouter un billet</h1>
|
|
<p class="font-bold text-gray-600 italic mb-8">{{ event.title }} — {{ category.name }}</p>
|
|
|
|
{% for message in app.flashes('error') %}
|
|
<div class="flash-error"><p class="font-black text-sm">{{ message }}</p></div>
|
|
{% endfor %}
|
|
|
|
<div class="card-brutal">
|
|
<form method="post" action="{{ path('app_account_event_add_billet', {id: event.id, categoryId: category.id}) }}" enctype="multipart/form-data" class="form-col">
|
|
<div>
|
|
<label for="billet_type" class="text-xs font-black uppercase tracking-widest form-label">Type</label>
|
|
<select id="billet_type" name="type" class="form-input focus:border-indigo-600">
|
|
<option value="billet">Billet</option>
|
|
<option value="reservation_brocante">Reservation brocante</option>
|
|
<option value="vote">Vote</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label for="billet_name" class="text-xs font-black uppercase tracking-widest form-label">Nom du billet</label>
|
|
<input type="text" id="billet_name" name="name" required class="form-input focus:border-indigo-600" placeholder="Ex: Entree adulte, Pass VIP...">
|
|
</div>
|
|
|
|
<div>
|
|
<label for="billet_price" class="text-xs font-black uppercase tracking-widest form-label">Prix HT (€)</label>
|
|
<input type="number" id="billet_price" name="price_ht" required min="0" step="0.01" class="form-input focus:border-indigo-600" placeholder="Ex: 15.00">
|
|
</div>
|
|
|
|
<div>
|
|
<label for="billet_quantity" class="text-xs font-black uppercase tracking-widest form-label">Quantite disponible (vide = illimite)</label>
|
|
<input type="number" id="billet_quantity" name="quantity" min="1" class="form-input focus:border-indigo-600" placeholder="Illimite">
|
|
</div>
|
|
|
|
{% include 'account/_billet_commission.html.twig' %}
|
|
|
|
<div>
|
|
<label for="billet_description" class="text-xs font-black uppercase tracking-widest form-label">Description</label>
|
|
<textarea id="billet_description" name="description" rows="3" class="form-input focus:border-indigo-600" placeholder="Description optionnelle du billet..."></textarea>
|
|
</div>
|
|
|
|
<div>
|
|
<label for="billet_picture" class="text-xs font-black uppercase tracking-widest form-label">Image</label>
|
|
<input type="file" id="billet_picture" name="picture" accept="image/png, image/jpeg, image/webp, image/gif" class="form-file">
|
|
</div>
|
|
|
|
<div class="flex items-center gap-3">
|
|
<input type="checkbox" id="billet_generated" name="is_generated_billet" value="1" class="w-5 h-5 border-2 border-gray-900 cursor-pointer" checked>
|
|
<label for="billet_generated" class="text-sm font-black uppercase tracking-widest cursor-pointer">Generer un billet PDF</label>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-3">
|
|
<input type="checkbox" id="billet_exit" name="has_defined_exit" value="1" class="w-5 h-5 border-2 border-gray-900 cursor-pointer">
|
|
<label for="billet_exit" class="text-sm font-black uppercase tracking-widest cursor-pointer">Sortie definitive</label>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-3">
|
|
<input type="checkbox" id="billet_not_buyable" name="not_buyable" value="1" class="w-5 h-5 border-2 border-gray-900 cursor-pointer">
|
|
<label for="billet_not_buyable" class="text-sm font-black uppercase tracking-widest cursor-pointer">Non achetable</label>
|
|
</div>
|
|
|
|
<div>
|
|
<button type="submit" class="btn-brutal font-black uppercase text-sm tracking-widest hover:bg-indigo-600 hover:text-white transition-all">
|
|
Ajouter le billet
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|