secutorpro's picture
🐳 27/05 - 09:36 - tu as cree le dashboard le saas  il reste le crm
98f1d13 verified
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CRM - ComSync Pro</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/feather-icons"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.net.min.js"></script>
<link rel="stylesheet" href="style.css">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
body { font-family: 'Inter', sans-serif; }
.crm-card {
background: rgba(31, 41, 55, 0.7);
backdrop-filter: blur(20px);
border: 1px solid rgba(75, 85, 99, 0.4);
transition: all 0.3s ease;
}
.crm-card:hover {
background: rgba(31, 41, 55, 0.85);
border-color: rgba(75, 85, 99, 0.6);
transform: translateY(-2px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
}
.status-dot {
width: 10px;
height: 10px;
border-radius: 50%;
border: 2px solid #1f2937;
}
.status-online { background: #10b981; }
.status-offline { background: #ef4444; }
.status-busy { background: #f59e0b; }
.contact-row {
transition: all 0.2s ease;
}
.contact-row:hover {
background: rgba(55, 65, 81, 0.5);
}
.action-btn {
transition: all 0.2s;
}
.action-btn:hover {
transform: scale(1.1);
}
.pipeline-bar {
height: 8px;
border-radius: 4px;
background: #374151;
overflow: hidden;
}
.pipeline-fill {
height: 100%;
border-radius: 4px;
transition: width 0.5s ease;
}
</style>
</head>
<body class="bg-gray-900 text-gray-100 overflow-x-hidden">
<div id="vanta-bg" class="fixed inset-0 z-0"></div>
<script src="components/navbar.js"></script>
<custom-navbar></custom-navbar>
<main class="relative z-10 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- Header -->
<div class="mb-8 slide-in-bottom">
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<h2 class="text-3xl font-bold text-white mb-2">CRM - Relations & Équipes</h2>
<p class="text-gray-400">Gérez vos contacts, équipes et missions clients</p>
</div>
<div class="flex items-center gap-3">
<div class="bg-gray-700/50 rounded-xl p-2 flex items-center space-x-3">
<i data-feather="search" class="w-5 h-5 text-gray-400 ml-2"></i>
<input type="text" id="crmSearch" class="bg-transparent border-none text-white text-sm focus:outline-none w-48" placeholder="Rechercher contact...">
</div>
<button onclick="openAddContactModal()" class="btn-primary flex items-center">
<i data-feather="plus" class="w-4 h-4 mr-2"></i>
Ajouter Contact
</button>
</div>
</div>
</div>
<!-- Stats -->
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
<div class="metric-card glass-card rounded-xl p-6">
<div class="flex items-center justify-between mb-4">
<div class="p-3 bg-blue-900/30 rounded-xl">
<i data-feather="users" class="w-6 h-6 text-blue-400"></i>
</div>
<span class="text-green-400 text-sm">+5 cette semaine</span>
</div>
<p class="text-3xl font-bold text-white" id="stat-contacts">42</p>
<p class="text-gray-400">Contacts</p>
</div>
<div class="metric-card glass-card rounded-xl p-6">
<div class="flex items-center justify-between mb-4">
<div class="p-3 bg-purple-900/30 rounded-xl">
<i data-feather="briefcase" class="w-6 h-6 text-purple-400"></i>
</div>
<span class="text-yellow-400 text-sm">3 en négociation</span>
</div>
<p class="text-3xl font-bold text-white" id="stat-deals">12</p>
<p class="text-gray-400">Contrats Actifs</p>
</div>
<div class="metric-card glass-card rounded-xl p-6">
<div class="flex items-center justify-between mb-4">
<div class="p-3 bg-green-900/30 rounded-xl">
<i data-feather="target" class="w-6 h-6 text-green-400"></i>
</div>
<span class="text-green-400 text-sm">87% taux</span>
</div>
<p class="text-3xl font-bold text-white" id="stat-missions">8</p>
<p class="text-gray-400">Missions en Cours</p>
</div>
<div class="metric-card glass-card rounded-xl p-6">
<div class="flex items-center justify-between mb-4">
<div class="p-3 bg-orange-900/30 rounded-xl">
<i data-feather="clock" class="w-6 h-6 text-orange-400"></i>
</div>
<span class="text-gray-400 text-sm">Ce mois</span>
</div>
<p class="text-3xl font-bold text-white">156h</p>
<p class="text-gray-400">Heures Facturées</p>
</div>
</div>
<!-- Main Grid -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
<!-- Left: Contacts & Teams -->
<div class="lg:col-span-2 space-y-6">
<!-- Filters -->
<div class="glass-card rounded-xl p-4 flex flex-wrap gap-3">
<button onclick="filterContacts('all')" class="px-4 py-2 bg-blue-600 rounded-lg text-sm font-medium text-white">Tous</button>
<button onclick="filterContacts('client')" class="px-4 py-2 bg-gray-700/50 rounded-lg text-sm font-medium text-gray-300 hover:bg-gray-700">Clients</button>
<button onclick="filterContacts('team')" class="px-4 py-2 bg-gray-700/50 rounded-lg text-sm font-medium text-gray-300 hover:bg-gray-700">Équipe</button>
<button onclick="filterContacts('provider')" class="px-4 py-2 bg-gray-700/50 rounded-lg text-sm font-medium text-gray-300 hover:bg-gray-700">Fournisseurs</button>
<button onclick="filterContacts('online')" class="px-4 py-2 bg-gray-700/50 rounded-lg text-sm font-medium text-gray-300 hover:bg-gray-700 flex items-center gap-2">
<span class="w-2 h-2 bg-green-500 rounded-full"></span> En ligne
</button>
</div>
<!-- Contacts List -->
<div class="glass-card rounded-xl overflow-hidden" id="contacts-container">
<div class="p-6 border-b border-gray-700/50 flex items-center justify-between">
<h3 class="text-xl font-semibold text-white flex items-center">
<i data-feather="users" class="w-5 h-5 mr-3 text-blue-400"></i>
Annuaire
</h3>
<span class="text-sm text-gray-400" id="contacts-count">42 contacts</span>
</div>
<div id="contacts-list" class="divide-y divide-gray-700/50">
<!-- Filled by JS -->
</div>
</div>
</div>
<!-- Right Column -->
<div class="space-y-6">
<!-- Quick Actions CRM -->
<div class="glass-card rounded-xl p-6">
<h3 class="text-xl font-semibold text-white mb-4 flex items-center">
<i data-feather="zap" class="w-5 h-5 mr-3 text-yellow-400"></i>
Actions Rapides
</h3>
<div class="grid grid-cols-2 gap-3">
<button onclick="quickAction('mission')" class="p-4 bg-blue-600/20 hover:bg-blue-600/30 rounded-xl border border-blue-500/30 transition-all group text-center">
<i data-feather="target" class="w-6 h-6 text-blue-400 mb-2 mx-auto"></i>
<p class="text-sm font-medium text-blue-400">Nouvelle Mission</p>
</button>
<button onclick="quickAction('deal')" class="p-4 bg-purple-600/20 hover:bg-purple-600/30 rounded-xl border border-purple-500/30 transition-all group text-center">
<i data-feather="file-text" class="w-6 h-6 text-purple-400 mb-2 mx-auto"></i>
<p class="text-sm font-medium text-purple-400">Nouveau Contrat</p>
</button>
<button onclick="quickAction('quote')" class="p-4 bg-green-600/20 hover:bg-green-600/30 rounded-xl border border-green-500/30 transition-all group text-center">
<i data-feather="dollar-sign" class="w-6 h-6 text-green-400 mb-2 mx-auto"></i>
<p class="text-sm font-medium text-green-400">Devis</p>
</button>
<button onclick="quickAction('invoice')" class="p-4 bg-orange-600/20 hover:bg-orange-600/30 rounded-xl border border-orange-500/30 transition-all group text-center">
<i data-feather="credit-card" class="w-6 h-6 text-orange-400 mb-2 mx-auto"></i>
<p class="text-sm font-medium text-orange-400">Facture</p>
</button>
</div>
</div>
<!-- Pipeline / Missions -->
<div class="glass-card rounded-xl p-6">
<h3 class="text-xl font-semibold text-white mb-4 flex items-center">
<i data-feather="trending-up" class="w-5 h-5 mr-3 text-green-400"></i>
Pipeline Missions
</h3>
<div class="space-y-4">
<div>
<div class="flex justify-between text-sm mb-1">
<span class="text-gray-300">Prospection</span>
<span class="text-blue-400">4</span>
</div>
<div class="pipeline-bar">
<div class="pipeline-fill bg-blue-500" style="width: 20%"></div>
</div>
</div>
<div>
<div class="flex justify-between text-sm mb-1">
<span class="text-gray-300">Proposition</span>
<span class="text-yellow-400">3</span>
</div>
<div class="pipeline-bar">
<div class="pipeline-fill bg-yellow-500" style="width: 35%"></div>
</div>
</div>
<div>
<div class="flex justify-between text-sm mb-1">
<span class="text-gray-300">Négociation</span>
<span class="text-orange-400">2</span>
</div>
<div class="pipeline-bar">
<div class="pipeline-fill bg-orange-500" style="width: 50%"></div>
</div>
</div>
<div>
<div class="flex justify-between text-sm mb-1">
<span class="text-gray-300">Mission Active</span>
<span class="text-green-400">8</span>
</div>
<div class="pipeline-bar">
<div class="pipeline-fill bg-green-500" style="width: 80%"></div>
</div>
</div>
<div>
<div class="flex justify-between text-sm mb-1">
<span class="text-gray-300">Terminée</span>
<span class="text-purple-400">24</span>
</div>
<div class="pipeline-bar">
<div class="pipeline-fill bg-purple-500" style="width: 95%"></div>
</div>
</div>
</div>
</div>
<!-- Recent CRM Activity -->
<div class="glass-card rounded-xl p-6">
<h3 class="text-xl font-semibold text-white mb-4 flex items-center">
<i data-feather="activity" class="w-5 h-5 mr-3 text-purple-400"></i>
Activité CRM
</h3>
<div class="space-y-3" id="crm-activity">
<div class="flex items-start gap-3 p-3 bg-gray-700/30 rounded-lg">
<div class="w-2 h-2 bg-blue-500 rounded-full mt-2 shrink-0"></div>
<div>
<p class="text-sm text-white">Nouveau contrat signé — <span class="text-gray-400">SecurEvent SAS</span></p>
<p class="text-xs text-gray-500">il y a 12 min</p>
</div>
</div>
<div class="flex items-start gap-3 p-3 bg-gray-700/30 rounded-lg">
<div class="w-2 h-2 bg-green-500 rounded-full mt-2 shrink-0"></div>
<div>
<p class="text-sm text-white">Mission "Surveillance Secteur 7" démarrée</p>
<p class="text-xs text-gray-500">il y a 45 min</p>
</div>
</div>
<div class="flex items-start gap-3 p-3 bg-gray-700/30 rounded-lg">
<div class="w-2 h-2 bg-yellow-500 rounded-full mt-2 shrink-0"></div>
<div>
<p class="text-sm text-white">Devis envoyé — <span class="text-gray-400">Logistique Nord</span></p>
<p class="text-xs text-gray-500">il y a 2h</p>
</div>
</div>
<div class="flex items-start gap-3 p-3 bg-gray-700/30 rounded-lg">
<div class="w-2 h-2 bg-purple-500 rounded-full mt-2 shrink-0"></div>
<div>
<p class="text-sm text-white">Nouveau contact ajouté — <span class="text-gray-400">Ahmed B.</span></p>
<p class="text-xs text-gray-500">il y a 3h</p>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
<!-- Add Contact Modal -->
<div id="add-contact-modal" class="fixed inset-0 bg-black/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
<div class="bg-gray-800 rounded-xl max-w-lg w-full border border-gray-700 shadow-2xl">
<div class="p-6 border-b border-gray-700 flex justify-between items-center">
<h2 class="text-xl font-semibold">Ajouter un Contact</h2>
<button onclick="closeAddContactModal()" class="text-gray-400 hover:text-white">
<i data-feather="x" class="w-6 h-6"></i>
</button>
</div>
<div class="p-6 space-y-4">
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium mb-2">Type *</label>
<select id="contact-type" class="w-full bg-gray-700 rounded-lg px-3 py-2 border border-gray-600 text-white">
<option value="client">Client</option>
<option value="team">Équipe</option>
<option value="provider">Fournisseur</option>
</select>
</div>
<div>
<label class="block text-sm font-medium mb-2">Statut</label>
<select id="contact-status" class="w-full bg-gray-700 rounded-lg px-3 py-2 border border-gray-600 text-white">
<option value="online">En ligne</option>
<option value="offline">Hors ligne</option>
<option value="busy">Occupé</option>
</select>
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium mb-2">Prénom *</label>
<input type="text" id="contact-firstname" class="w-full bg-gray-700 rounded-lg px-3 py-2 border border-gray-600 text-white" placeholder="Prénom">
</div>
<div>
<label class="block text-sm font-medium mb-2">Nom *</label>
<input type="text" id="contact-lastname" class="w-full bg-gray-700 rounded-lg px-3 py-2 border border-gray-600 text-white" placeholder="Nom">
</div>
</div>
<div>
<label class="block text-sm font-medium mb-2">Email</label>
<input type="email" id="contact-email" class="w-full bg-gray-700 rounded-lg px-3 py-2 border border-gray-600 text-white" placeholder="email@entreprise.com">
</div>
<div>
<label class="block text-sm font-medium mb-2">Téléphone</label>
<input type="tel" id="contact-phone" class="w-full bg-gray-700 rounded-lg px-3 py-2 border border-gray-600 text-white" placeholder="+33 6 12 34 56 78">
</div>
<div>
<label class="block text-sm font-medium mb-2">Entreprise / Rôle</label>
<input type="text" id="contact-role" class="w-full bg-gray-700 rounded-lg px-3 py-2 border border-gray-600 text-white" placeholder="Ex: Sécurité Plus SA — Chef d'équipe">
</div>
</div>
<div class="p-6 border-t border-gray-700 flex justify-end space-x-3">
<button onclick="closeAddContactModal()" class="btn-secondary">Annuler</button>
<button onclick="saveContact()" class="btn-primary">Enregistrer</button>
</div>
</div>
</div>
<script>
// Demo contacts data
let contacts = [
{ id: 'C001', type: 'team', firstname: 'John', lastname: 'Smith', email: 'j.smith@comsync.pro', phone: '+33 6 12 34 56 78', role: 'Opérations — Secteur 7', status: 'online', lastSeen: 'Actif' },
{ id: 'C002', type: 'team', firstname: 'Maria', lastname: 'Garcia', email: 'm.garcia@comsync.pro', phone: '+33 6 23 45 67 89', role: 'Logistique — Base', status: 'online', lastSeen: 'Actif' },
{ id: 'C003', type: 'client', firstname: 'Pierre', lastname: 'Dubois', email: 'p.dubois@secur-event.fr', phone: '+33 1 42 56 78 90', role: 'SecurEvent SAS — Directeur', status: 'offline', lastSeen: 'il y a 2h' },
{ id: 'C004', type: 'team', firstname: 'Ahmed', lastname: 'Benjelloun', email: 'a.benjelloun@comsync.pro', phone: '+33 6 34 56 78 90', role: 'Sécurité — Périmètre', status: 'busy', lastSeen: 'En mission' },
{ id: 'C005', type: 'client', firstname: 'Sophie', lastname: 'Laurent', email: 's.laurent@log-nord.fr', phone: '+33 3 20 45 67 89', role: 'Logistique Nord — Responsable', status: 'offline', lastSeen: 'il y a 5h' },
{ id: 'C006', type: 'provider', firstname: 'Thomas', lastname: 'Bernard', email: 't.bernard@radiopro.fr', phone: '+33 4 91 23 45 67', role: 'RadioPro — Technicien', status: 'online', lastSeen: 'Actif' },
{ id: 'C007', type: 'team', firstname: 'Robert', lastname: 'Johnson', email: 'r.johnson@comsync.pro', phone: '+33 6 45 67 89 01', role: 'Support — Secteur 3', status: 'offline', lastSeen: 'il y a 1h' },
{ id: 'C008', type: 'client', firstname: 'Claire', lastname: 'Moreau', email: 'c.moreau@industech.fr', phone: '+33 2 40 12 34 56', role: 'Industech — Sécurité', status: 'online', lastSeen: 'Actif' }
];
let currentFilter = 'all';
function renderContacts() {
const list = document.getElementById('contacts-list');
const searchVal = document.getElementById('crmSearch').value.toLowerCase();
let filtered = contacts;
if (currentFilter !== 'all') {
if (currentFilter === 'online') {
filtered = contacts.filter(c => c.status === 'online');
} else {
filtered = contacts.filter(c => c.type === currentFilter);
}
}
if (searchVal) {
filtered = filtered.filter(c =>
(c.firstname + ' ' + c.lastname).toLowerCase().includes(searchVal) ||
c.role.toLowerCase().includes(searchVal) ||
c.email.toLowerCase().includes(searchVal)
);
}
document.getElementById('contacts-count').textContent = filtered.length + ' contact(s)';
if (filtered.length === 0) {
list.innerHTML = '<div class="p-8 text-center text-gray-500">Aucun contact trouvé</div>';
feather.replace();
return;
}
list.innerHTML = filtered.map(c => `
<div class="contact-row flex items-center gap-4 p-4 cursor-pointer group">
<div class="relative shrink-0">
<div class="w-12 h-12 bg-gradient-to-r from-blue-500 to-purple-600 rounded-full flex items-center justify-center text-white font-bold text-sm">
${c.firstname[0]}${c.lastname[0]}
</div>
<div class="absolute -bottom-1 -right-1 w-3.5 h-3.5 rounded-full border-2 border-gray-800 ${c.status === 'online' ? 'bg-green-500' : c.status === 'busy' ? 'bg-yellow-500' : 'bg-red-500'}"></div>
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2 mb-0.5">
<h4 class="text-white font-medium truncate">${c.firstname} ${c.lastname}</h4>
<span class="text-xs px-2 py-0.5 rounded-full ${c.type === 'client' ? 'bg-blue-900/30 text-blue-400' : c.type === 'team' ? 'bg-green-900/30 text-green-400' : 'bg-purple-900/30 text-purple-400'}">${c.type}</span>
</div>
<p class="text-sm text-gray-400 truncate">${c.role}</p>
<div class="flex items-center gap-3 mt-1 text-xs text-gray-500">
<span class="flex items-center gap-1"><i data-feather="mail" class="w-3 h-3"></i> ${c.email}</span>
<span class="flex items-center gap-1"><i data-feather="phone" class="w-3 h-3"></i> ${c.phone}</span>
</div>
</div>
<div class="flex items-center gap-2 opacity-0 group-hover:opacity-100 transition-opacity shrink-0">
<button onclick="event.stopPropagation(); callContact('${c.id}')" class="action-btn p-2 bg-blue-600 rounded-lg hover:bg-blue-700" title="Appeler">
<i data-feather="phone" class="w-4 h-4"></i>
</button>
<button onclick="event.stopPropagation(); messageContact('${c.id}')" class="action-btn p-2 bg-green-600 rounded-lg hover:bg-green-700" title="Message">
<i data-feather="message-square" class="w-4 h-4"></i>
</button>
<button onclick="event.stopPropagation(); locateContact('${c.id}')" class="action-btn p-2 bg-purple-600 rounded-lg hover:bg-purple-700" title="Localiser">
<i data-feather="map-pin" class="w-4 h-4"></i>
</button>
</div>
<div class="text-right shrink-0 min-w-[80px]">
<p class="text-xs ${c.status === 'online' ? 'text-green-400' : c.status === 'busy' ? 'text-yellow-400' : 'text-gray-500'} capitalize">${c.status}</p>
<p class="text-xs text-gray-500">${c.lastSeen}</p>
</div>
</div>
`).join('');
feather.replace();
}
function filterContacts(type) {
currentFilter = type;
// update buttons
document.querySelectorAll('#contacts-container button').forEach(btn => {
btn.classList.remove('bg-blue-600', 'text-white');
btn.classList.add('bg-gray-700/50', 'text-gray-300');
});
event.target.classList.remove('bg-gray-700/50', 'text-gray-300');
event.target.classList.add('bg-blue-600', 'text-white');
renderContacts();
}
function callContact(id) {
const c = contacts.find(x => x.id === id);
alert(`📞 Appel vers ${c.firstname} ${c.lastname}\n${c.phone}`);
}
function messageContact(id) {
const c = contacts.find(x => x.id === id);
alert(`💬 Message rapide à ${c.firstname} ${c.lastname}\nEmail: ${c.email}`);
}
function locateContact(id) {
const c = contacts.find(x => x.id === id);
alert(`📍 Localisation de ${c.firstname} ${c.lastname}\nDernière position: Base Camp`);
}
function quickAction(type) {
const labels = { mission: 'Nouvelle Mission', deal: 'Nouveau Contrat', quote: 'Nouveau Devis', invoice: 'Nouvelle Facture' };
alert(`${labels[type]}\n\nCette action ouvrirait le formulaire complet dans une implementation complète.`);
}
function openAddContactModal() {
document.getElementById('add-contact-modal').classList.remove('hidden');
}
function closeAddContactModal() {
document.getElementById('add-contact-modal').classList.add('hidden');
}
function saveContact() {
const type = document.getElementById('contact-type').value;
const status = document.getElementById('contact-status').value;
const firstname = document.getElementById('contact-firstname').value.trim();
const lastname = document.getElementById('contact-lastname').value.trim();
if (!firstname || !lastname) { alert('Veuillez remplir le prénom et le nom'); return; }
contacts.unshift({
id: 'C' + Date.now().toString().slice(-3),
type,
status,
firstname,
lastname,
email: document.getElementById('contact-email').value || '-',
phone: document.getElementById('contact-phone').value || '-',
role: document.getElementById('contact-role').value || '-',
lastSeen: 'À l\'instant'
});
closeAddContactModal();
renderContacts();
alert('Contact ajouté avec succès !');
}
// Search listener
document.getElementById('crmSearch').addEventListener('input', renderContacts);
// Vanta
if (typeof VANTA !== 'undefined') {
VANTA.NET({
el: "#vanta-bg",
mouseControls: true, touchControls: true, gyroControls: false,
minHeight: 200, minWidth: 200, scale: 1, scaleMobile: 1,
color: 0x3b82f6, backgroundColor: 0x111827,
points: 10, maxDistance: 22, spacing: 18
});
}
feather.replace();
renderContacts();
</script>
</body>
</html>