✨ feat(etl/mission): Implémente la gestion des missions avec dashboard, liste et démarrage
This commit is contained in:
111
templates/etl/edl.twig
Normal file
111
templates/etl/edl.twig
Normal file
@@ -0,0 +1,111 @@
|
||||
{% extends 'etl/base.twig' %}
|
||||
|
||||
{% block title %}État des Lieux - #{{ mission.numReservation }}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="space-y-6 animate-in fade-in slide-in-from-bottom-4 duration-500">
|
||||
|
||||
{# HEADER #}
|
||||
<div class="flex items-center gap-4">
|
||||
<a href="{{ path('etl_contrat_view', {id: mission.id}) }}" class="w-10 h-10 bg-white rounded-xl border border-slate-100 flex items-center justify-center text-slate-400 hover:text-blue-600 transition-all shadow-sm">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" /></svg>
|
||||
</a>
|
||||
<div>
|
||||
<h1 class="text-xl font-black text-slate-900 tracking-tight">État des Lieux</h1>
|
||||
<p class="text-[10px] font-bold text-slate-400 uppercase tracking-widest">Réf: #{{ mission.numReservation }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# PHOTOS / VIDEOS #}
|
||||
<div class="bg-white rounded-[2rem] p-6 border border-slate-100 shadow-sm">
|
||||
<h3 class="text-xs font-black text-slate-400 uppercase tracking-widest mb-4">Photos & Vidéos</h3>
|
||||
|
||||
<div class="grid grid-cols-3 gap-2 mb-4">
|
||||
{% for file in etatLieux.files %}
|
||||
<div class="aspect-square bg-slate-100 rounded-xl overflow-hidden relative group">
|
||||
{% if file.type == 'photo' %}
|
||||
<img src="{{ vich_uploader_asset(file, 'file') }}" alt="Photo" class="w-full h-full object-cover cursor-pointer lightbox-trigger" data-type="photo" data-src="{{ vich_uploader_asset(file, 'file') }}">
|
||||
{% else %}
|
||||
<div class="w-full h-full relative cursor-pointer lightbox-trigger" data-type="video" data-src="{{ vich_uploader_asset(file, 'file') }}">
|
||||
<video src="{{ vich_uploader_asset(file, 'file') }}" class="w-full h-full object-cover pointer-events-none"></video>
|
||||
<div class="absolute inset-0 flex items-center justify-center bg-black/20 text-white pointer-events-none">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" /><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form action="{{ path('etl_edl_delete_file', {id: mission.id, fileId: file.id}) }}" method="post" class="absolute top-1 right-1 z-10">
|
||||
<button type="submit" class="bg-red-500/80 text-white p-1 rounded-full hover:bg-red-600 transition-colors shadow-sm" onclick="return confirm('Supprimer ce fichier ?')">
|
||||
<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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /></svg>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="col-span-3 text-center py-4 text-slate-400 text-xs italic">Aucun média ajouté.</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{# LIGHTBOX MODAL #}
|
||||
<div id="lightbox-modal" class="fixed inset-0 z-[100] bg-black/90 hidden items-center justify-center p-2 backdrop-blur-sm transition-opacity duration-300 opacity-0 pointer-events-none">
|
||||
<button id="lightbox-close" class="absolute top-4 right-4 text-white/80 hover:text-white z-50 p-2 bg-black/50 rounded-full">
|
||||
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /></svg>
|
||||
</button>
|
||||
<div class="w-full h-full flex items-center justify-center relative">
|
||||
<img id="lightbox-img" src="" class="max-w-full max-h-full object-contain rounded-lg shadow-2xl hidden" alt="Full view">
|
||||
<video id="lightbox-video" src="" controls class="max-w-full max-h-full rounded-lg shadow-2xl hidden"></video>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form action="{{ path('etl_edl_add_file', {id: mission.id}) }}" method="post" enctype="multipart/form-data" class="space-y-4">
|
||||
<div class="flex gap-2">
|
||||
<label class="flex-1 cursor-pointer bg-blue-50 text-blue-600 rounded-xl border border-blue-100 py-3 flex items-center justify-center gap-2 text-xs font-bold uppercase tracking-wide hover:bg-blue-100 transition-colors">
|
||||
<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="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z" /><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 13a3 3 0 11-6 0 3 3 0 016 0z" /></svg>
|
||||
Sélectionner Photos
|
||||
<input type="file" name="photos[]" accept="image/*" multiple class="hidden">
|
||||
</label>
|
||||
<label class="flex-1 cursor-pointer bg-purple-50 text-purple-600 rounded-xl border border-purple-100 py-3 flex items-center justify-center gap-2 text-xs font-bold uppercase tracking-wide hover:bg-purple-100 transition-colors">
|
||||
<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="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" /></svg>
|
||||
Sélectionner Vidéos
|
||||
<input type="file" name="videos[]" accept="video/*" multiple class="hidden">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="w-full py-3 bg-slate-900 text-white rounded-xl text-xs font-bold uppercase tracking-wide hover:bg-slate-800 transition-colors shadow-lg">
|
||||
Envoyer les fichiers
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{# COMMENTAIRES #}
|
||||
<div class="bg-white rounded-[2rem] p-6 border border-slate-100 shadow-sm">
|
||||
<h3 class="text-xs font-black text-slate-400 uppercase tracking-widest mb-4">Commentaires</h3>
|
||||
|
||||
<div class="space-y-3 mb-4">
|
||||
{% for comment in etatLieux.comments %}
|
||||
<div class="bg-slate-50 p-3 rounded-xl">
|
||||
<p class="text-[10px] font-bold text-slate-400 mb-1">{{ comment.createdAt|date('d/m H:i') }}</p>
|
||||
<p class="text-sm text-slate-700">{{ comment.content }}</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-center py-2 text-slate-400 text-xs italic">Aucun commentaire.</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<form action="{{ path('etl_edl_add_comment', {id: mission.id}) }}" method="post" class="flex gap-2">
|
||||
<input type="text" name="content" placeholder="Votre commentaire..." class="flex-1 bg-slate-50 border border-slate-200 rounded-xl px-4 py-3 text-sm focus:outline-none focus:border-blue-500 transition-colors" required>
|
||||
<button type="submit" class="bg-slate-900 text-white p-3 rounded-xl hover:bg-slate-700 transition-colors">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" /></svg>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{# ACTION TERMINER #}
|
||||
<form action="{{ path('etl_edl_finish', {id: mission.id}) }}" method="post">
|
||||
<button type="submit" class="w-full py-4 bg-emerald-500 hover:bg-emerald-600 text-white rounded-2xl font-black uppercase text-sm tracking-widest shadow-lg shadow-emerald-500/30 transition-all active:scale-95 flex items-center justify-center gap-3">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" /></svg>
|
||||
Terminer l'état des lieux
|
||||
</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
{% block body %}
|
||||
<div class="space-y-6 animate-in fade-in slide-in-from-bottom-4 duration-500">
|
||||
|
||||
|
||||
{# HEADER #}
|
||||
<div class="flex items-center gap-4">
|
||||
<a href="{{ path('etl_home') }}" class="w-10 h-10 bg-white rounded-xl border border-slate-100 flex items-center justify-center text-slate-400 hover:text-blue-600 transition-all shadow-sm">
|
||||
@@ -25,13 +25,93 @@
|
||||
</button>
|
||||
</form>
|
||||
{% elseif mission.etatLieux.status == 'delivery_progress' %}
|
||||
<div class="w-full py-4 bg-amber-500/10 border border-amber-500/20 text-amber-500 rounded-2xl font-black uppercase text-sm tracking-widest flex items-center justify-center gap-3">
|
||||
<span class="relative flex h-3 w-3">
|
||||
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-amber-400 opacity-75"></span>
|
||||
<span class="relative inline-flex rounded-full h-3 w-3 bg-amber-500"></span>
|
||||
</span>
|
||||
Livraison en cours
|
||||
<form action="{{ path('etl_mission_finish', {id: mission.id}) }}" method="post">
|
||||
<button type="submit" class="w-full py-4 bg-amber-500 hover:bg-amber-600 text-white rounded-2xl font-black uppercase text-sm tracking-widest shadow-lg shadow-amber-500/30 transition-all active:scale-95 flex items-center justify-center gap-3">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" /></svg>
|
||||
Confirmer la livraison
|
||||
</button>
|
||||
</form>
|
||||
{% elseif mission.etatLieux.status == 'delivery_done' %}
|
||||
<div class="w-full py-4 bg-emerald-500/10 border border-emerald-500/20 text-emerald-600 rounded-2xl font-black uppercase text-sm tracking-widest flex items-center justify-center gap-3 mb-6">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
|
||||
Livraison Terminée
|
||||
</div>
|
||||
|
||||
{% set is_chorus = (mission.devis and 'Chorus' in mission.devis.paymentMethod) %}
|
||||
|
||||
{% if not is_chorus %}
|
||||
|
||||
{# CAUTION #}
|
||||
<div class="bg-white rounded-[2rem] p-6 border border-slate-100 shadow-sm mb-4">
|
||||
<h3 class="text-xs font-black text-slate-400 uppercase tracking-widest mb-4">Caution</h3>
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-2xl font-black text-slate-900">{{ totalCaution|number_format(2, ',', ' ') }}€</span>
|
||||
{% if cautionPaid %}
|
||||
<span class="px-3 py-1 bg-emerald-100 text-emerald-600 rounded-lg text-[10px] font-black uppercase">Reçue</span>
|
||||
{% else %}
|
||||
<form action="{{ path('etl_mission_caution', {id: mission.id}) }}" method="post">
|
||||
<button type="submit" class="px-4 py-2 bg-slate-900 text-white rounded-xl text-[10px] font-black uppercase tracking-wide hover:bg-blue-600 transition-colors">
|
||||
Valider Réception
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# SOLDE #}
|
||||
{% if solde > 0 %}
|
||||
<div class="bg-slate-900 rounded-[2rem] p-6 text-white shadow-xl mb-4">
|
||||
<div class="flex justify-between items-start mb-4">
|
||||
<div>
|
||||
<p class="text-[10px] font-black text-blue-400 uppercase tracking-widest mb-1">Reste à payer</p>
|
||||
<p class="text-3xl font-black">{{ solde|number_format(2, ',', ' ') }}€</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Paiement en ligne #}
|
||||
<form action="{{ path('elt_mission_confirme', {id: mission.id}) }}" method="post" class="mb-4">
|
||||
<button type="submit" class="w-full py-3 bg-blue-600 hover:bg-blue-500 text-white rounded-xl font-bold text-xs uppercase tracking-wider shadow-lg shadow-blue-900/50 transition-all active:scale-95 flex items-center justify-center gap-2">
|
||||
<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="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"/></svg>
|
||||
Paiement Carte Bancaire
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{# Paiements Manuels #}
|
||||
<p class="text-[9px] font-bold text-slate-500 uppercase tracking-widest mb-2 text-center">Encaissement direct</p>
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
{% for method in ['Chèque', 'Espèces', 'Autre'] %}
|
||||
<form action="{{ path('etl_mission_manual_pay', {id: mission.id}) }}" method="post">
|
||||
<input type="hidden" name="method" value="{{ method }}">
|
||||
<button type="submit" class="w-full py-2 bg-slate-800 hover:bg-slate-700 text-slate-300 hover:text-white rounded-xl font-bold text-[10px] uppercase tracking-wide transition-all active:scale-95 border border-slate-700">
|
||||
{{ method }}
|
||||
</button>
|
||||
</form>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="bg-emerald-500/10 border border-emerald-500/20 text-emerald-500 rounded-[2rem] p-6 text-center mb-4">
|
||||
<p class="text-xs font-black uppercase tracking-widest">Solde Réglé</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
{# ACTION EDL #}
|
||||
{% if is_chorus or solde <= 0 %}
|
||||
<form action="{{ path('etl_mission_edl_start', {id: mission.id}) }}" method="post" class="mt-6">
|
||||
<button type="submit" class="w-full py-4 bg-indigo-600 hover:bg-indigo-500 text-white rounded-2xl font-black uppercase text-sm tracking-widest shadow-lg shadow-indigo-600/30 transition-all active:scale-95 flex items-center justify-center gap-3">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" /></svg>
|
||||
Commencer l'état des lieux
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
{% elseif mission.etatLieux.status == 'edl_progress' %}
|
||||
<a href="{{ path('etl_mission_edl', {id: mission.id}) }}" class="w-full py-4 bg-indigo-600 hover:bg-indigo-500 text-white rounded-2xl font-black uppercase text-sm tracking-widest shadow-lg shadow-indigo-600/30 transition-all active:scale-95 flex items-center justify-center gap-3 mb-6">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" /></svg>
|
||||
Reprendre l'état des lieux
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{# DATES #}
|
||||
@@ -68,7 +148,7 @@
|
||||
{# ADRESSE & GPS #}
|
||||
<div class="bg-white rounded-[2rem] p-6 border border-slate-100 shadow-sm">
|
||||
<h3 class="text-xs font-black text-slate-400 uppercase tracking-widest mb-4">Lieu de l'événement</h3>
|
||||
|
||||
|
||||
<div class="mb-6">
|
||||
<p class="font-bold text-slate-900 text-sm leading-relaxed">
|
||||
{{ mission.addressEvent }}<br>
|
||||
@@ -92,7 +172,7 @@
|
||||
{# PRODUCTS & OPTIONS #}
|
||||
<div class="bg-white rounded-[2rem] p-6 border border-slate-100 shadow-sm">
|
||||
<h3 class="text-xs font-black text-slate-400 uppercase tracking-widest mb-4">Matériel & Options</h3>
|
||||
|
||||
|
||||
<div class="space-y-3">
|
||||
{% for line in mission.contratsLines %}
|
||||
{% if 'livraison' not in line.name|lower %}
|
||||
@@ -127,7 +207,7 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{# DETAILS / NOTES #}
|
||||
{% if mission.details or mission.notes %}
|
||||
<div class="bg-white rounded-[2rem] p-6 border border-slate-100 shadow-sm">
|
||||
|
||||
Reference in New Issue
Block a user