325 lines
16 KiB
HTML
Executable File
325 lines
16 KiB
HTML
Executable File
<!DOCTYPE html>
|
||
<html lang="fr">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>CRM H3R7 - Gestion Prospects</title>
|
||
<style>
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
body { font-family: -apple-system, BlinkMacSystemFont, sans-serif; background: #1a1a2e; color: #eee; padding: 20px; }
|
||
header { background: linear-gradient(90deg, #e94560, #7b2cbf); padding: 20px; text-align: center; margin: -20px -20px 20px; border-radius: 0 0 20px 20px; }
|
||
h1 { font-size: 28px; }
|
||
|
||
.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 15px; margin-bottom: 20px; }
|
||
.stat-card { background: #16213e; padding: 20px; border-radius: 12px; text-align: center; }
|
||
.stat-num { font-size: 32px; font-weight: bold; color: #00d9ff; }
|
||
.stat-label { color: #aaa; font-size: 14px; margin-top: 5px; }
|
||
|
||
.add-form { background: #16213e; padding: 20px; border-radius: 12px; margin-bottom: 20px; }
|
||
.add-form h2 { margin-bottom: 15px; color: #fff; }
|
||
.form-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
|
||
input, select { width: 100%; padding: 12px; margin: 5px 0; background: #0f3460; border: 1px solid #333; border-radius: 8px; color: #fff; }
|
||
button { padding: 12px 20px; background: #00d9ff; border: none; border-radius: 8px; color: #000; font-weight: bold; cursor: pointer; }
|
||
button:hover { background: #00b8d4; }
|
||
|
||
.prospects-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 15px; }
|
||
.prospect-card { background: #16213e; padding: 15px; border-radius: 12px; border-left: 4px solid #00d9ff; }
|
||
.prospect-card.nouveau { border-left-color: #ffd700; }
|
||
.prospect-card.quali { border-left-color: #00d9ff; }
|
||
.prospect-card.proposition { border-left-color: #7b2cbf; }
|
||
.prospect-card.gagne { border-left-color: #00ff88; }
|
||
.prospect-card.perdu { border-left-color: #e94560; }
|
||
|
||
.prospect-name { font-size: 18px; font-weight: bold; margin-bottom: 5px; }
|
||
.prospect-entreprise { color: #aaa; font-size: 14px; margin-bottom: 10px; }
|
||
.prospect-info { font-size: 13px; color: #888; margin: 3px 0; }
|
||
.prospect-secteur { background: #7b2cbf; padding: 3px 8px; border-radius: 10px; font-size: 11px; display: inline-block; margin-top: 8px; }
|
||
|
||
.score { float: right; }
|
||
.score-star { color: #ffd700; }
|
||
|
||
.actions { margin-top: 10px; display: flex; gap: 5px; }
|
||
.btn-edit { background: #7b2cbf; color: #fff; padding: 5px 10px; border: none; border-radius: 5px; cursor: pointer; font-size: 12px; }
|
||
.btn-delete { background: #e94560; color: #fff; padding: 5px 10px; border: none; border-radius: 5px; cursor: pointer; font-size: 12px; }
|
||
|
||
.filter-bar { margin-bottom: 15px; display: flex; gap: 10px; }
|
||
.filter-btn { padding: 8px 15px; background: #0f3460; border: none; border-radius: 8px; color: #fff; cursor: pointer; }
|
||
.filter-btn.active { background: #00d9ff; color: #000; }
|
||
|
||
.modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); }
|
||
.modal.show { display: flex; justify-content: center; align-items: center; }
|
||
.modal-content { background: #16213e; padding: 30px; border-radius: 15px; width: 500px; max-width: 90%; }
|
||
.modal h2 { margin-bottom: 20px; }
|
||
.modal-buttons { display: flex; gap: 10px; margin-top: 20px; }
|
||
.btn-cancel { background: #e94560; }
|
||
|
||
.badge { padding: 3px 8px; border-radius: 5px; font-size: 11px; margin-left: 5px; }
|
||
.badge-nouveau { background: #ffd700; color: #000; }
|
||
.badge-quali { background: #00d9ff; color: #000; }
|
||
.badge-proposition { background: #7b2cbf; color: #fff; }
|
||
.badge-gagne { background: #00ff88; color: #000; }
|
||
.badge-perdu { background: #e94560; color: #fff; }
|
||
|
||
a { color: #00d9ff; }
|
||
|
||
.home-btn{position:fixed;top:10px;left:10px;z-index:9999;background:linear-gradient(135deg,#00d9ff,#7b2cbf);color:#fff;border:none;border-radius:8px;padding:10px 15px;font-size:14px;cursor:pointer;text-decoration:none;display:flex;align-items:center;gap:8px;box-shadow:0 2px 10px rgba(0,217,255,0.3);transition:all 0.3s;font-family:-apple-system,BlinkMacSystemFont,sans-serif}.home-btn:hover{transform:translateY(-2px);box-shadow:0 4px 15px rgba(0,217,255,0.5)}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<a href="https://portal-kolifee.duckdns.org/" class="home-btn" title="Retour au portail"><span style="font-size:18px">🏠</span><span>Accueil</span></a>
|
||
<header>
|
||
<h1>📊 CRM H3R7 - Gestion des Prospects</h1>
|
||
</header>
|
||
|
||
<div class="stats" id="stats">
|
||
<div class="stat-card">
|
||
<div class="stat-num" id="total-prospects">0</div>
|
||
<div class="stat-label">Total Prospects</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-num" id="nouveaux">0</div>
|
||
<div class="stat-label">Nouveaux</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-num" id="qualifies">0</div>
|
||
<div class="stat-label">Qualifiés</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-num" id="gagnes">0</div>
|
||
<div class="stat-label">Gagnés</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="add-form">
|
||
<h2>➕ Nouveau Prospect</h2>
|
||
<div class="form-row">
|
||
<input type="text" id="nom" placeholder="Nom du contact *">
|
||
<input type="text" id="entreprise" placeholder="Entreprise *">
|
||
<input type="text" id="secteur" placeholder="Secteur (Artisan, Boulanger, Marketing...)">
|
||
</div>
|
||
<div class="form-row">
|
||
<input type="tel" id="tel" placeholder="Téléphone">
|
||
<input type="email" id="email" placeholder="Email">
|
||
<select id="source">
|
||
<option value="">Source ?</option>
|
||
<option value="appeler">Appel téléphonique</option>
|
||
<option value="email">Email</option>
|
||
<option value="site">Site web</option>
|
||
<option value="recommandation">Recommandation</option>
|
||
<option value="autre">Autre</option>
|
||
</select>
|
||
</div>
|
||
<button onclick="addProspect()">Ajouter le Prospect</button>
|
||
</div>
|
||
|
||
<div class="filter-bar">
|
||
<button class="filter-btn active" onclick="filterProspects('tous')">Tous</button>
|
||
<button class="filter-btn" onclick="filterProspects('nouveau')">Nouveau</button>
|
||
<button class="filter-btn" onclick="filterProspects('qualifie')">Qualifié</button>
|
||
<button class="filter-btn" onclick="filterProspects('proposition')">Proposition</button>
|
||
<button class="filter-btn" onclick="filterProspects('gagne')">Gagné</button>
|
||
<button class="filter-btn" onclick="filterProspects('perdu')">Perdu</button>
|
||
<select id="catFilter" onchange="renderProspects()" style="padding:8px;background:#0f3460;color:#fff;border:1px solid #333;border-radius:8px;">
|
||
<option value="all">Toutes catégories</option>
|
||
<option value="Restaurant">Restaurants</option>
|
||
<option value="Boulangerie">Boulangeries</option>
|
||
<option value="Garage">Garages</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="prospects-grid" id="prospects-grid">
|
||
Chargement...
|
||
</div>
|
||
|
||
<a href="/">← Retour au Portail</a>
|
||
|
||
<!-- Modal Edit -->
|
||
<div class="modal" id="editModal">
|
||
<div class="modal-content">
|
||
<h2>✏️ Modifier Prospect</h2>
|
||
<input type="hidden" id="edit-id">
|
||
<div class="form-row">
|
||
<input type="text" id="edit-nom" placeholder="Nom">
|
||
<input type="text" id="edit-entreprise" placeholder="Entreprise">
|
||
<input type="text" id="edit-secteur" placeholder="Secteur">
|
||
</div>
|
||
<div class="form-row">
|
||
<input type="tel" id="edit-tel" placeholder="Téléphone">
|
||
<input type="email" id="edit-email" placeholder="Email">
|
||
<select id="edit-statut">
|
||
<option value="nouveau">Nouveau</option>
|
||
<option value="qualifie">Qualifié</option>
|
||
<option value="proposition">Proposition</option>
|
||
<option value="gagne">Gagné</option>
|
||
<option value="perdu">Perdu</option>
|
||
</select>
|
||
</div>
|
||
<textarea id="edit-notes" placeholder="Notes..." rows="3" style="width:100%;padding:12px;margin:5px 0;background:#0f3460;border:1px solid #333;border-radius:8px;color:#fff;"></textarea>
|
||
<div class="form-row">
|
||
<select id="edit-score">
|
||
<option value="0">Score: 0</option>
|
||
<option value="1">Score: ⭐</option>
|
||
<option value="2">Score: ⭐⭐</option>
|
||
<option value="3">Score: ⭐⭐⭐</option>
|
||
<option value="4">Score: ⭐⭐⭐⭐</option>
|
||
<option value="5">Score: ⭐⭐⭐⭐⭐</option>
|
||
</select>
|
||
</div>
|
||
<div class="modal-buttons">
|
||
<button onclick="saveEdit()">💾 Sauvegarder</button>
|
||
<button class="btn-cancel" onclick="closeModal()">Annuler</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
const API = '/crm/api/prospects';
|
||
let allProspects = [];
|
||
let currentFilter = 'tous';
|
||
|
||
function loadProspects() {
|
||
fetch(API)
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
allProspects = data.prospects || [];
|
||
updateStats();
|
||
renderProspects();
|
||
});
|
||
}
|
||
|
||
function updateStats() {
|
||
document.getElementById('total-prospects').textContent = allProspects.length;
|
||
document.getElementById('nouveaux').textContent = allProspects.filter(p => p.statut === 'nouveau').length;
|
||
document.getElementById('qualifies').textContent = allProspects.filter(p => p.statut === 'qualifie' || p.statut === 'proposition').length;
|
||
document.getElementById('gagnes').textContent = allProspects.filter(p => p.statut === 'gagne').length;
|
||
}
|
||
|
||
function renderProspects() {
|
||
let prospects = allProspects;
|
||
// Filter by category
|
||
const catFilter = document.getElementById('catFilter').value;
|
||
if (catFilter !== 'all') {
|
||
prospects = prospects.filter(p => p.categorie === catFilter || p.secteur === catFilter || p.entreprise === catFilter);
|
||
}
|
||
// Filter by status
|
||
if (currentFilter !== 'tous') {
|
||
prospects = prospects.filter(p => p.statut === currentFilter);
|
||
}
|
||
|
||
const grid = document.getElementById('prospects-grid');
|
||
if (prospects.length === 0) {
|
||
grid.innerHTML = '<div style="grid-column:1/-1;text-align:center;color:#666;padding:40px;">Aucun prospect</div>';
|
||
return;
|
||
}
|
||
|
||
grid.innerHTML = prospects.map((p, i) => `
|
||
<div class="prospect-card ${p.statut}" data-idx="${i}">
|
||
<span class="score">${'⭐'.repeat(p.score || 0)}</span>
|
||
<div class="prospect-name">${p.nom || ''}</div>
|
||
<div class="prospect-entreprise">${p.entreprise || ''}</div>
|
||
<div class="prospect-info">📞 ${p.tel || '-'}</div>
|
||
<div class="prospect-info">📍 <a href="#" onclick="event.preventDefault(); var addr = this.textContent.replace('📍 ', '').trim(); window.open('https://www.google.com/maps/search/?api=1&query=' + encodeURIComponent(addr), '_blank');" data-idx="${i}" style="color:#00d9ff;text-decoration:none;">${p.adresse || '-'}</a></div>
|
||
<div class="prospect-info">✉️ ${p.email || '-'}</div>
|
||
<div class="prospect-info">📅 ${p.created ? p.created.substring(0,10) : '-'}</div>
|
||
<span class="prospect-secteur">${p.secteur || 'Autre'}</span>
|
||
<span class="badge badge-${p.statut}">${p.statut}</span>
|
||
<div class="actions">
|
||
<button class="btn-edit" onclick="openEdit('${p.id}')">✏️</button>
|
||
<button class="btn-delete" onclick="deleteProspect('${p.id}')">🗑️</button>
|
||
</div>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
function addProspect() {
|
||
const nom = document.getElementById('nom').value;
|
||
const entreprise = document.getElementById('entreprise').value;
|
||
if (!nom || !entreprise) {
|
||
alert('Nom et entreprise requis!');
|
||
return;
|
||
}
|
||
|
||
fetch(API, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
nom: nom,
|
||
entreprise: entreprise,
|
||
secteur: document.getElementById('secteur').value,
|
||
tel: document.getElementById('tel').value,
|
||
email: document.getElementById('email').value,
|
||
source: document.getElementById('source').value,
|
||
statut: 'nouveau',
|
||
score: 0
|
||
})
|
||
}).then(() => {
|
||
// Clear form
|
||
document.getElementById('nom').value = '';
|
||
document.getElementById('entreprise').value = '';
|
||
document.getElementById('secteur').value = '';
|
||
document.getElementById('tel').value = '';
|
||
document.getElementById('email').value = '';
|
||
loadProspects();
|
||
});
|
||
}
|
||
|
||
function openEdit(id) {
|
||
const p = allProspects.find(x => x.id === id);
|
||
if (!p) return;
|
||
|
||
document.getElementById('edit-id').value = id;
|
||
document.getElementById('edit-nom').value = p.nom || '';
|
||
document.getElementById('edit-entreprise').value = p.entreprise || '';
|
||
document.getElementById('edit-secteur').value = p.secteur || '';
|
||
document.getElementById('edit-tel').value = p.tel || '';
|
||
document.getElementById('edit-email').value = p.email || '';
|
||
document.getElementById('edit-statut').value = p.statut || 'nouveau';
|
||
document.getElementById('edit-notes').value = p.notes || '';
|
||
document.getElementById('edit-score').value = p.score || 0;
|
||
|
||
document.getElementById('editModal').classList.add('show');
|
||
}
|
||
|
||
function closeModal() {
|
||
document.getElementById('editModal').classList.remove('show');
|
||
}
|
||
|
||
function saveEdit() {
|
||
const id = parseInt(document.getElementById('edit-id').value);
|
||
|
||
fetch(API + '/' + id, {
|
||
method: 'PUT',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
nom: document.getElementById('edit-nom').value,
|
||
entreprise: document.getElementById('edit-entreprise').value,
|
||
secteur: document.getElementById('edit-secteur').value,
|
||
tel: document.getElementById('edit-tel').value,
|
||
email: document.getElementById('edit-email').value,
|
||
statut: document.getElementById('edit-statut').value,
|
||
notes: document.getElementById('edit-notes').value,
|
||
score: parseInt(document.getElementById('edit-score').value)
|
||
})
|
||
}).then(() => {
|
||
closeModal();
|
||
loadProspects();
|
||
});
|
||
}
|
||
|
||
function deleteProspect(id) {
|
||
if (!confirm('Supprimer ce prospect?')) return;
|
||
fetch(API + '/' + id, { method: 'DELETE' })
|
||
.then(() => loadProspects());
|
||
}
|
||
|
||
function filterProspects(filter) {
|
||
currentFilter = filter;
|
||
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
|
||
event.target.classList.add('active');
|
||
renderProspects();
|
||
}
|
||
|
||
loadProspects();
|
||
</script>
|
||
</body>
|
||
</html>
|