Files
ludikevent_crm/templates/dashboard/products.twig
Serreau Jovann 6e60c9d4b3 feat(product): Ajoute gestion des images, indexation et synchro Stripe produits
Ce commit ajoute la gestion des images pour les produits, l'indexation des produits pour la recherche et la synchronisation avec Stripe. Ajoute un formulaire de création/édition de produits avec gestion de l'image, l'indexation pour la recherche, et la synchronisation des produits avec Stripe. Gère les uploads d'images.
2026-01-16 14:23:53 +01:00

143 lines
8.9 KiB
Twig

{% extends 'dashboard/base.twig' %}
{% block title %}Catalogue Produits{% endblock %}
{% block title_header %}Gestion du <span class="text-blue-500">Matériel</span>{% endblock %}
{% block actions %}
<div class="flex items-center space-x-3">
<a href="{{ path('app_crm_product_add') }}" class="flex items-center space-x-2 px-6 py-3 bg-blue-600 hover:bg-blue-500 text-white text-[10px] font-black uppercase tracking-[0.2em] rounded-xl transition-all shadow-lg shadow-blue-600/20 group">
<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="M12 4v16m8-8H4" />
</svg>
<span>Nouveau Produit</span>
</a>
</div>
{% endblock %}
{% block body %}
<div class="backdrop-blur-xl bg-[#1e293b]/40 border border-white/5 rounded-[2.5rem] overflow-hidden shadow-2xl animate-in fade-in duration-700">
<div class="overflow-x-auto">
<table class="w-full text-left border-collapse">
<thead>
<tr class="border-b border-white/5 bg-black/20">
<th class="px-6 py-5 text-[10px] font-black text-slate-500 uppercase tracking-[0.2em]">Visuel & Réf</th>
<th class="px-6 py-5 text-[10px] font-black text-slate-500 uppercase tracking-[0.2em]">Désignation</th>
<th class="px-6 py-5 text-[10px] font-black text-slate-500 uppercase tracking-[0.2em]">Catégorie</th>
<th class="px-6 py-5 text-[10px] font-black text-slate-500 uppercase tracking-[0.2em]">Stripe</th>
<th class="px-6 py-5 text-[10px] font-black text-slate-500 uppercase tracking-[0.2em] text-center">Tarif J1</th>
<th class="px-6 py-5 text-[10px] font-black text-slate-500 uppercase tracking-[0.2em] text-center">Install.</th>
<th class="px-6 py-5 text-[10px] font-black text-slate-500 uppercase tracking-[0.2em] text-right">Actions</th>
</tr>
</thead>
<tbody class="divide-y divide-white/5">
{% for product in products %}
<tr class="group hover:bg-white/[0.02] transition-colors">
{# VISUEL & REF #}
<td class="px-6 py-4">
<div class="flex items-center space-x-4">
<div class="h-12 w-12 rounded-xl overflow-hidden border border-white/10 bg-slate-900 flex-shrink-0">
{% if product.imageName %}
<img src="{{ vich_uploader_asset(product, 'imageFile') | imagine_filter('webp') }}" class="h-full w-full object-cover">
{% else %}
<div class="h-full w-full flex items-center justify-center bg-slate-800 text-slate-600">
<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="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" /></svg>
</div>
{% endif %}
</div>
<span class="text-[10px] font-mono font-bold text-blue-500 tracking-wider">{{ product.ref }}</span>
</div>
</td>
{# NOM #}
<td class="px-6 py-4">
<span class="text-sm font-bold text-white group-hover:text-blue-400 transition-colors capitalize">
{{ product.name }}
</span>
</td>
{# CATEGORIE #}
<td class="px-6 py-4">
<span class="px-3 py-1 bg-white/5 border border-white/10 rounded-lg text-[9px] font-black text-slate-400 uppercase tracking-widest">
{{ product.category }}
</span>
</td>
{# STRIPE #}
<td class="px-6 py-4">
{% if product.productId %}
<div class="flex items-center text-[8px] font-black text-emerald-400 uppercase tracking-widest">
<span class="h-1.5 w-1.5 rounded-full bg-emerald-500 mr-2 shadow-[0_0_8px_rgba(16,185,129,0.5)]"></span>
Synchronisé
</div>
{% else %}
<div class="flex items-center text-[8px] font-black text-rose-500 uppercase tracking-widest opacity-60">
<span class="h-1.5 w-1.5 rounded-full bg-rose-500 mr-2 animate-pulse"></span>
En attente
</div>
{% endif %}
</td>
{# PRIX #}
<td class="px-6 py-4 text-center">
<span class="text-sm font-black text-emerald-400">
{{ product.priceDay|number_format(2, ',', ' ') }}
</span>
</td>
{# INSTALLATION (MONTANT) #}
<td class="px-6 py-4 text-center">
<span class="text-[10px] font-bold {{ product.installation > 0 ? 'text-amber-500' : 'text-slate-600' }}">
{{ product.installation > 0 ? product.installation|number_format(2, ',', ' ') ~ '€' : 'OFFERT' }}
</span>
</td>
{# ACTIONS #}
<td class="px-6 py-4 text-right">
<div class="flex items-center justify-end space-x-2">
<a href="{{ path('app_crm_product_edit', {id: product.id}) }}" class="p-2 bg-blue-600/10 hover:bg-blue-600 text-blue-500 hover:text-white rounded-xl transition-all border border-blue-500/20 shadow-lg shadow-blue-600/5">
<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="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" /></svg>
</a>
<a href="{{ path('app_crm_product_delete', {id: product.id}) }}?_token={{ csrf_token('delete' ~ product.id) }}"
data-turbo-method="post"
data-turbo-confirm="Confirmer la suppression définitive de '{{ product.name }}' ?"
class="p-2 bg-rose-500/10 hover:bg-rose-500 text-rose-500 hover:text-white rounded-xl transition-all border border-rose-500/20 shadow-lg shadow-rose-500/5">
<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>
</a>
</div>
</td>
</tr>
{% else %}
<tr>
<td colspan="7" class="py-24 text-center">
<p class="text-slate-500 italic uppercase tracking-[0.2em] text-[10px] font-black">Aucun produit dans le catalogue</p>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{# PAGINATION #}
{% if products.getTotalItemCount is defined and products.getTotalItemCount > products.getItemNumberPerPage %}
<div class="mt-8 flex justify-center custom-pagination">
{{ knp_pagination_render(products) }}
</div>
{% endif %}
<style>
.custom-pagination nav ul { @apply flex space-x-2; }
.custom-pagination nav ul li span,
.custom-pagination nav ul li a {
@apply px-4 py-2 rounded-xl bg-[#1e293b]/40 backdrop-blur-md border border-white/5 text-slate-400 text-xs font-bold transition-all;
}
.custom-pagination nav ul li.active span {
@apply bg-blue-600 border-blue-500 text-white shadow-lg shadow-blue-600/20;
}
.custom-pagination nav ul li a:hover {
@apply bg-white/10 text-white border-white/20;
}
</style>
{% endblock %}