Files
ludikevent_crm/templates/dashboard/contrats/list.twig
2026-02-16 09:12:49 +01:00

252 lines
22 KiB
Twig

{% extends 'dashboard/base.twig' %}
{% block title %}Contrats de locations{% endblock %}
{% block title_header %}Contrats de locations{% endblock %}
{% block actions %}
<div class="flex items-center gap-3">
<a href="{{ path('app_crm_contrats_create') }}"
class="group relative flex items-center gap-2 px-6 py-3 bg-gradient-to-br from-blue-600 to-indigo-700 hover:from-blue-500 hover:to-indigo-600 rounded-2xl text-white text-xs font-bold uppercase tracking-wider transition-all shadow-[0_8px_20px_-6px_rgba(37,99,235,0.5)]">
<svg class="w-4 h-4 transition-transform group-hover:rotate-12" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
Créer un contrat
</a>
</div>
{% endblock %}
{% block body %}
<div class="space-y-8 pb-20">
{# --- RECHERCHE --- #}
<div class="relative group max-w-2xl mx-auto">
<div class="absolute -inset-1 bg-gradient-to-r from-blue-500 to-indigo-500 rounded-[2rem] blur opacity-20 group-focus-within:opacity-40 transition duration-1000"></div>
<div class="relative flex items-center">
<div class="absolute inset-y-0 left-0 pl-6 flex items-center pointer-events-none">
<svg class="w-5 h-5 text-slate-400 group-focus-within:text-blue-400 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
</div>
<input type="text" id="searchContrat"
placeholder="Rechercher un contrat, un client ou une ville..."
class="w-full bg-slate-900/40 border border-white/10 backdrop-blur-2xl text-white text-sm rounded-[1.8rem] pl-14 pr-6 py-5 focus:outline-none focus:ring-2 focus:ring-blue-500/50 transition-all placeholder:text-slate-500">
</div>
</div>
<div id="contratsList" class="grid gap-6">
{% for contrat in contrats %}
{% set acompteOk = contratPaymentPay(contrat, 'accompte') %}
{% set cautionOk = contratPaymentPay(contrat, 'caution') %}
{% set soldeOk = contratPaymentPay(contrat, 'solde') %}
{% set cautionRelase = contratPaymentPay(contrat, 'caution_free') %}
{% set cautionEncaisser = contratPaymentPay(contrat, 'caution_recup') %}
<div class="contrat-card relative overflow-hidden group">
{# --- BANNIÈRE DE REFUS (Prioritaire) --- #}
{% if contrat.refused %}
<div class="bg-red-600/20 border-b border-red-500/30 px-8 py-3 flex items-center justify-between gap-3 backdrop-blur-md z-20 relative">
<div class="flex items-center gap-3">
<div class="p-1 bg-red-500 rounded-full">
<svg class="w-3 h-3 text-white" 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>
</div>
<div>
<p class="text-[10px] font-black text-red-400 uppercase tracking-widest leading-none">CONTRAT REFUSÉ</p>
<p class="text-xs text-red-100/80 mt-1 font-medium italic">
Motif : "{{ contrat.refusedRaison|default('Aucune raison spécifiée') }}"
</p>
</div>
</div>
</div>
{% endif %}
{# --- BANNIÈRE DE LITIGE (Si signature EDL refusée) --- #}
{% if contrat.etatLieux and contrat.etatLieux.status == 'edl_return_refused' and not contrat.refused %}
<div class="bg-red-600/20 border-b border-red-500/30 px-8 py-3 flex items-center justify-between gap-3 backdrop-blur-md">
<div class="flex items-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-red-400 opacity-75"></span>
<span class="relative inline-flex rounded-full h-3 w-3 bg-red-500"></span>
</span>
<div>
<p class="text-[10px] font-black text-red-400 uppercase tracking-widest leading-none">⚠️ Litige : Signature refusée par le client</p>
<p class="text-xs text-red-100/80 mt-1 font-medium italic">
Motif : "{{ contrat.etatLieux.raisonRefus|default('Non renseigné') }}"
</p>
</div>
</div>
<div class="px-3 py-1 bg-red-500 text-white rounded-lg text-[9px] font-black uppercase shadow-lg shadow-red-500/20">
Action Requise
</div>
</div>
{% endif %}
<div class="absolute -inset-px bg-gradient-to-r from-transparent via-white/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500 rounded-[2rem]"></div>
<div class="relative bg-white/[0.03] border border-white/10 backdrop-blur-md rounded-[2rem] transition-all duration-300 group-hover:bg-white/[0.06] group-hover:translate-y-[-2px] group-hover:shadow-2xl group-hover:shadow-blue-500/10 {{ contrat.refused ? 'opacity-50 grayscale' : '' }}">
<div class="grid grid-cols-1 lg:grid-cols-12 items-center">
{# 1. RÉFÉRENCE & STATUTS #}
<div class="lg:col-span-2 p-8 border-b lg:border-b-0 lg:border-r border-white/5 text-center lg:text-left">
<span class="text-[10px] font-bold text-blue-400 uppercase tracking-[0.2em] mb-1 block">Réservation</span>
<h3 class="text-white font-black text-xl tracking-tighter mb-3 {{ contrat.refused ? 'line-through decoration-red-500/50' : '' }}">{{ contrat.numReservation }}</h3>
<div class="flex flex-col gap-2 items-center lg:items-start">
{# Status Contrat #}
{% if contrat.refused %}
<div class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-red-500/10 border border-red-500/20 text-red-400 text-[9px] font-bold uppercase tracking-wider">
<span class="w-1 h-1 rounded-full bg-red-500"></span> Refusé
</div>
{% elseif contrat.isSigned %}
<div class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-emerald-500/10 border border-emerald-500/20 text-emerald-400 text-[9px] font-bold uppercase tracking-wider">
<span class="w-1 h-1 rounded-full bg-emerald-500"></span> Contrat Signé
</div>
{% else %}
<div class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-amber-500/10 border border-amber-500/20 text-amber-400 text-[9px] font-bold uppercase tracking-wider">
<span class="w-1 h-1 rounded-full bg-amber-500"></span> Attente Signature
</div>
{% endif %}
{# Status État des Lieux avec Traductions et Couleurs #}
{% if contrat.etatLieux %}
{% set status_config = {
'delivery_progress': { 'color': 'bg-blue-500/10 text-blue-400 border-blue-500/20', 'label': '🚚 Livraison en cours' },
'delivery_done': { 'color': 'bg-cyan-500/10 text-cyan-400 border-cyan-500/20', 'label': '📍 Livré' },
'edl_progress': { 'color': 'bg-indigo-500/10 text-indigo-400 border-indigo-500/20', 'label': '📝 EDL Installation...' },
'edl_validated': { 'color': 'bg-emerald-500/20 text-emerald-400 border-emerald-500/30', 'label': '✅ EDL Validé' },
'return_edl_progress': { 'color': 'bg-orange-500/10 text-orange-400 border-orange-500/20', 'label': '🔍 EDL Retour...' },
'edl_return_done': { 'color': 'bg-purple-500/10 text-purple-400 border-purple-500/20', 'label': '📦 Retour Effectué' },
'edl_return_finised': { 'color': 'bg-purple-500/10 text-purple-400 border-purple-500/20', 'label': '📦 Retour Effectué' },
'edl_return_refused': { 'color': 'bg-red-600 text-white border-red-500', 'label': '❌ Signature Refusée' }
} %}
{% set current_status = status_config[contrat.etatLieux.status] ?? { 'color': 'bg-slate-500/10 text-slate-400 border-white/5', 'label': contrat.etatLieux.status } %}
<div class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full border {{ current_status.color }} text-[9px] font-black uppercase tracking-wider shadow-sm">
{{ current_status.label }}
</div>
{% endif %}
</div>
</div>
{# 2. CLIENT #}
<div class="lg:col-span-2 p-8 border-b lg:border-b-0 lg:border-r border-white/5">
<div class="flex items-center gap-4">
<div class="w-12 h-12 bg-gradient-to-tr from-blue-600/20 to-indigo-600/20 rounded-2xl flex items-center justify-center border border-white/10 group-hover:scale-110 transition-transform">
<svg class="w-6 h-6 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" stroke-width="1.5"></path></svg>
</div>
<div>
<p class="text-white font-bold text-base tracking-tight uppercase">{{ contrat.customer.surname }} {{ contrat.customer.name }}</p>
<p class="text-slate-500 text-xs font-medium line-clamp-1">{{ contrat.customer.email }}</p>
</div>
</div>
</div>
{# 3. LIEU #}
<div class="lg:col-span-2 p-8 border-b lg:border-b-0 lg:border-r border-white/5">
<div class="flex flex-col">
<span class="text-[10px] font-bold text-slate-300 uppercase tracking-widest mb-1">Ville / CP</span>
<p class="text-slate-200 font-bold text-sm">{{ contrat.townEvent }}</p>
<p class="text-blue-500 font-black text-[11px]">{{ contrat.zipCodeEvent }}</p>
</div>
</div>
{# 4. PAIEMENTS & FINANCES #}
<div class="lg:col-span-4 p-4 bg-black/10 lg:bg-transparent border-b lg:border-b-0 lg:border-r border-white/5">
<div class="flex items-center justify-between gap-4 h-full">
<div class="flex flex-col justify-center min-w-[120px]">
{% if contrat.devis %}
<span class="text-sm font-black text-white">
{{ (contrat.devis|totalQuotoHT)|number_format(2, ',', ' ') }}€ HT
</span>
{% if contrat.devis.formule %}
<span class="text-[9px] text-blue-400 font-bold uppercase tracking-widest mt-1 truncate max-w-[150px]">
{{ contrat.devis.formule.name }}
</span>
{% endif %}
<span class="text-[9px] text-slate-500 font-bold mt-1">
Caution : {{ (contrat.devis|totalCaution)|number_format(2, ',', ' ') }}
</span>
{% else %}
<span class="text-xs text-slate-500 italic">Pas de devis</span>
{% endif %}
</div>
{# BADGES DE PAIEMENT #}
<div class="grid grid-cols-3 gap-2">
{# ACOMPTE #}
<div class="flex flex-col items-center gap-1">
<div class="w-7 h-7 rounded-lg flex items-center justify-center transition-all duration-300
{{ acompteOk ? 'bg-emerald-500/20 text-emerald-400 border border-emerald-500/30' : 'bg-red-500/10 text-red-500 border border-red-500/20' }}">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="{{ acompteOk ? 'M5 13l4 4L19 7' : 'M6 18L18 6M6 6l12 12' }}"></path></svg>
</div>
<span class="text-[7px] font-black uppercase tracking-tighter {{ acompteOk ? 'text-emerald-500' : 'text-red-500' }}">Acompte</span>
</div>
{# CAUTION #}
<div class="flex flex-col items-center gap-1">
{% if cautionEncaisser %}
<div class="w-7 h-7 rounded-lg flex items-center justify-center bg-red-600/30 text-red-400 border border-red-500/50">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"></path></svg>
</div>
<span class="text-[7px] font-black uppercase tracking-tighter text-red-400">Encaissée</span>
{% elseif cautionRelase %}
<div class="w-7 h-7 rounded-lg flex items-center justify-center bg-blue-500/20 text-blue-400 border border-blue-500/30">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M8 11V7a4 4 0 118 0m-4 8v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2z"></path></svg>
</div>
<span class="text-[7px] font-black uppercase tracking-tighter text-blue-400">Libérée</span>
{% elseif cautionOk %}
<div class="w-7 h-7 rounded-lg flex items-center justify-center bg-emerald-500/20 text-emerald-400 border border-emerald-500/30">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"></path></svg>
</div>
<span class="text-[7px] font-black uppercase tracking-tighter text-emerald-500">Détenue</span>
{% else %}
<div class="w-7 h-7 rounded-lg flex items-center justify-center bg-red-500/10 text-red-500 border border-red-500/20 animate-pulse">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path></svg>
</div>
<span class="text-[7px] font-black uppercase tracking-tighter text-red-500">Requise</span>
{% endif %}
</div>
{# SOLDE #}
<div class="flex flex-col items-center gap-1">
<div class="w-7 h-7 rounded-lg flex items-center justify-center transition-all duration-300
{{ soldeOk ? 'bg-emerald-500/20 text-emerald-400 border border-emerald-500/30' : 'bg-red-500/10 text-red-500 border border-red-500/20' }}">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="{{ soldeOk ? 'M5 13l4 4L19 7' : 'M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z' }}"></path></svg>
</div>
<span class="text-[7px] font-black uppercase tracking-tighter {{ soldeOk ? 'text-emerald-500' : 'text-red-500' }}">Solde</span>
</div>
</div>
</div>
</div>
{# 5. ACTIONS #}
<div class="lg:col-span-2 p-6 flex lg:flex-col items-center justify-center gap-3">
<a data-turbo="false" href="{{ path('app_crm_contrats_view', {id: contrat.id}) }}"
class="w-full lg:w-12 h-12 rounded-2xl bg-white/5 border border-white/10 flex items-center justify-center text-white hover:bg-blue-600 hover:border-blue-400 transition-all group/btn" title="Consulter">
<svg class="w-5 h-5 group-hover/btn:scale-110 transition-transform" 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><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"></path></svg>
</a>
{# Bouton Renvoyer Mail : Caché si signé OU refusé #}
{% if not contrat.isSigned and not contrat.refused %}
<a data-turbo="false" href="{{ path('app_crm_contrats', {idSend: contrat.id}) }}"
onclick="return confirm('Renvoyer les documents à {{ contrat.customer.email }} ?')"
class="w-full lg:w-12 h-12 rounded-2xl bg-white/5 border border-white/10 flex items-center justify-center text-white hover:bg-indigo-600 hover:border-indigo-400 transition-all group/btn" title="Renvoyer Mail">
<svg class="w-5 h-5 group-hover/btn:translate-x-1 group-hover/btn:-translate-y-1 transition-transform" 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"></path></svg>
</a>
{% endif %}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="mt-12 glass-pagination">
{{ knp_pagination_render(contrats) }}
</div>
</div>
{% endblock %}