feat(ansible): Ajoute les tâches cron pour recherche et sauvegarde

📝 style(templates): Crée un template de mail pour notifications de sauvegarde

🐛 fix(.gitignore): Exclut les fichiers de sauvegarde .zip

🎨 style(dashboard): Crée une page pour la gestion des sauvegardes

 feat(command): Implémente la commande de sauvegarde avec notification et rétention

🎨 style(dashboard): Améliore l'interface de recherche avec des effets visuels

 feat(dashboard): Ajoute une page pour la gestion des sauvegardes

 test(controller): Ajoute la logique de téléchargement et suppression des sauvegardes
This commit is contained in:
Serreau Jovann
2026-01-16 11:04:13 +01:00
parent dde4ec4217
commit 540bdc1d07
13 changed files with 724 additions and 57 deletions

View File

@@ -2,63 +2,98 @@
{% block title %}Recherche : {{ query }}{% endblock %}
{% block body %}
<div class="page-transition">
<div class="flex flex-col md:flex-row md:items-center justify-between mb-10 gap-6">
<div>
<p class="text-blue-600 font-bold text-[10px] uppercase tracking-[0.4em] mb-2">Recherche Multicritères</p>
<h1 class="text-3xl font-black text-slate-900 dark:text-white uppercase tracking-tighter">
Résultats pour <span class="text-blue-600">"{{ query }}"</span>
</h1>
</div>
<div class="bg-white dark:bg-slate-800 px-6 py-3 rounded-2xl border border-slate-200 dark:border-slate-700 shadow-sm">
<span class="text-xs font-bold text-slate-500 uppercase tracking-widest">
<span class="text-blue-600 text-lg mr-1">{{ results|length }}</span> correspondance(s)
</span>
</div>
</div>
{% block title_header %}Moteur de <span class="text-blue-500">Recherche</span>{% endblock %}
{# Formulaire de mise à jour #}
<div class="mb-12">
<form action="{{ path('app_crm_search') }}" method="GET" class="relative max-w-2xl">
<input type="text" name="q" value="{{ query }}" placeholder="Rechercher à nouveau..."
class="w-full pl-6 pr-40 py-4 bg-white dark:bg-slate-900 border-2 border-slate-100 dark:border-slate-800 focus:border-blue-600 focus:ring-0 rounded-2xl text-slate-900 dark:text-white font-medium shadow-xl shadow-slate-200/40 dark:shadow-none transition-all outline-none">
<button type="submit" class="absolute right-2 top-2 bottom-2 px-6 bg-slate-900 dark:bg-blue-600 text-white text-[10px] font-bold uppercase tracking-widest rounded-xl hover:bg-blue-600 transition-all">
Actualiser
</button>
</form>
</div>
{% if results is not empty %}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{% for item in results %}
<div class="bg-white dark:bg-[#1e293b] p-6 rounded-[2.5rem] border border-slate-200 dark:border-slate-800 shadow-[0_10px_40px_rgba(0,0,0,0.02)] hover:shadow-xl hover:translate-y-[-4px] transition-all group">
<div class="flex items-center space-x-4 mb-6">
<div class="w-14 h-14 bg-slate-100 dark:bg-slate-800 rounded-2xl flex items-center justify-center text-slate-900 dark:text-white font-black text-xl border border-slate-200 dark:border-slate-700 uppercase">
{{ item.initials }}
</div>
<div class="overflow-hidden">
<span class="px-2 py-0.5 bg-indigo-50 dark:bg-indigo-900/30 text-[8px] font-bold text-indigo-600 uppercase rounded-md border border-indigo-100 dark:border-indigo-800">
{{ item.type }}
</span>
<h3 class="font-bold text-slate-900 dark:text-white text-lg leading-none truncate mt-2">{{ item.title }}</h3>
<p class="text-xs text-slate-400 mt-1 truncate">{{ item.subtitle }}</p>
</div>
</div>
<div class="flex items-center justify-between pt-5 border-t border-slate-50 dark:border-slate-800">
<div class="text-[10px] font-mono text-slate-400">#{{ item.id }}</div>
<a href="{{ item.link }}" class="flex items-center space-x-2 px-4 py-2 bg-slate-900 dark:bg-slate-700 text-white rounded-xl text-[10px] font-bold uppercase tracking-widest hover:bg-blue-600 transition-colors">
<span>Accéder</span>
</a>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="p-20 bg-white dark:bg-slate-900 rounded-[3rem] text-center border-2 border-dashed border-slate-100 dark:border-slate-800">
<p class="text-slate-400 font-medium italic">Aucune donnée trouvée pour cette recherche.</p>
</div>
{% endif %}
{% block actions %}
<div class="backdrop-blur-md bg-white/5 border border-white/10 px-5 py-2 rounded-xl shadow-xl">
<span class="text-xs font-bold text-slate-400 uppercase tracking-widest">
<span class="text-blue-500 text-lg mr-1">{{ results|length }}</span> Résultat(s)
</span>
</div>
{% endblock %}
{% block body %}
<div class="relative min-h-screen">
{# Orbes décoratifs pour l'effet Glass #}
<div class="absolute -top-24 -left-20 w-72 h-72 bg-blue-600/10 rounded-full blur-[120px] -z-10 pointer-events-none"></div>
<div class="absolute top-1/2 right-0 w-96 h-96 bg-indigo-600/5 rounded-full blur-[130px] -z-10 pointer-events-none"></div>
<div class="space-y-10 relative z-10">
{# Barre de recherche flottante #}
<div class="max-w-3xl">
<form action="{{ path('app_crm_search') }}" method="GET" class="relative group">
<div class="absolute inset-y-0 left-6 flex items-center pointer-events-none">
<svg class="w-5 h-5 text-slate-500 group-focus-within:text-blue-500 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
</svg>
</div>
<input type="text" name="q" value="{{ query }}" placeholder="Rechercher un client, un devis, une tâche..."
class="w-full pl-16 pr-40 py-5 bg-[#1e293b]/40 backdrop-blur-xl border border-white/10 focus:border-blue-500/50 focus:ring-0 rounded-[2rem] text-white font-medium shadow-2xl transition-all outline-none">
<button type="submit" class="absolute right-3 top-3 bottom-3 px-8 bg-blue-600 text-white text-[10px] font-black uppercase tracking-[0.2em] rounded-2xl hover:bg-blue-700 hover:shadow-[0_0_20px_rgba(37,99,235,0.4)] transition-all">
Actualiser
</button>
</form>
</div>
{% if results is not empty %}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{% for item in results %}
<div class="backdrop-blur-xl bg-[#1e293b]/30 hover:bg-[#1e293b]/60 p-8 rounded-[2.5rem] border border-white/5 hover:border-blue-500/30 shadow-xl transition-all duration-500 group relative overflow-hidden">
{# Effet de lueur au survol #}
<div class="absolute -right-10 -top-10 w-32 h-32 bg-blue-600/5 rounded-full blur-3xl group-hover:bg-blue-600/20 transition-all"></div>
<div class="flex items-start justify-between mb-8 relative z-10">
<div class="w-14 h-14 bg-white/5 rounded-2xl flex items-center justify-center text-blue-500 font-black text-xl border border-white/10 group-hover:scale-110 group-hover:border-blue-500/50 transition-all duration-500 uppercase shadow-inner">
{{ item.initials }}
</div>
<span class="px-4 py-1.5 bg-blue-500/10 text-[9px] font-black text-blue-400 uppercase tracking-widest rounded-full border border-blue-500/20 backdrop-blur-md">
{{ item.type }}
</span>
</div>
<div class="space-y-2 mb-8 relative z-10">
<h3 class="font-bold text-white text-xl leading-tight group-hover:text-blue-400 transition-colors tracking-tight truncate">
{{ item.title }}
</h3>
<p class="text-xs text-slate-400 font-medium tracking-wide truncate">
{{ item.subtitle }}
</p>
</div>
<div class="flex items-center justify-between pt-6 border-t border-white/5 relative z-10">
<div class="flex flex-col">
<span class="text-[9px] font-bold text-slate-600 uppercase tracking-[0.2em]">Identifiant</span>
<span class="text-xs font-mono text-slate-400">#{{ item.id }}</span>
</div>
<a href="{{ item.link }}" class="flex items-center space-x-2 px-6 py-3 bg-white/5 hover:bg-blue-600 text-white border border-white/10 hover:border-blue-500 rounded-2xl text-[10px] font-black uppercase tracking-[0.2em] transition-all duration-300 shadow-lg">
<span>Voir Fiche</span>
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M14 5l7 7m0 0l-7 7m7-7H3"/></svg>
</a>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="py-32 backdrop-blur-xl bg-[#1e293b]/20 rounded-[3rem] text-center border-2 border-dashed border-white/5 shadow-2xl">
<div class="w-20 h-20 bg-white/5 rounded-full flex items-center justify-center mx-auto mb-6">
<svg class="w-10 h-10 text-slate-600" 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"/>
</svg>
</div>
<h3 class="text-white font-bold text-xl uppercase tracking-tighter">Aucun résultat trouvé</h3>
<p class="text-slate-500 text-sm mt-2">Désolé, nous n'avons trouvé aucune correspondance pour "{{ query }}".</p>
</div>
{% endif %}
</div>
</div>
<style>
/* Texture subtile de grain pour le fond Glassmorphism */
.backdrop-blur-xl {
background-image: radial-gradient(rgba(255,255,255,0.03) 1px, transparent 0);
background-size: 8px 8px;
}
</style>
{% endblock %}