Initial commit: existing turf_saas codebase

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
ML Engineer
2026-04-25 17:18:43 +02:00
commit ed07c8a3d1
137 changed files with 36398 additions and 0 deletions

205
crm_simple.html Executable file
View File

@@ -0,0 +1,205 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>CRM H3R7</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:-apple-system,sans-serif;background:#1a1a2e;color:#eee;padding:20px}
header{background:linear-gradient(90deg,#e94560,#7b2cbf);padding:20px;text-align:center;margin:-20px -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;color:#00d9ff}
.stat-label{color:#aaa;font-size:14px}
.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.contacte{border-left-color:#00d9ff}
.prospect-card.rdv{border-left-color:#ff6b6b}
.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}
.prospect-entreprise{color:#aaa;font-size:14px}
.prospect-info{font-size:13px;color:#888;margin:3px 0}
.actions{margin-top:10px;display:flex;gap:5px}
.btn-edit,.btn-delete{padding:5px 10px;border:none;border-radius:5px;cursor:pointer;font-size:12px}
.btn-edit{background:#7b2cbf;color:#fff}
.btn-delete{background:#e94560;color:#fff}
.filter-bar{margin-bottom:15px;display:flex;gap:10px;flex-wrap:wrap}
.filter-btn{padding:8px 15px;background:#0f3460;border:none;border-radius:8px;color:#fff;cursor:pointer}
.filter-btn.active{background:#00d9ff;color:#000}
select{padding:8px;background:#0f3460;color:#fff;border:1px solid #333;border-radius:8px}
.modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.8);z-index:1000}
.modal.show{display:block}
.modal-content{background:#16213e;padding:30px;border-radius:12px;width:90%;max-width:500px;margin:50px auto}
.form-group{margin-bottom:15px}
.form-group label{display:block;color:#aaa;margin-bottom:5px}
.form-group input,.form-group select,.form-group textarea{width:100%;padding:10px;background:#0f3460;border:1px solid #333;border-radius:8px;color:#fff}
.modal-buttons{display:flex;gap:10px;margin-top:20px}
.modal-buttons button{flex:1;padding:12px;border:none;border-radius:8px;cursor:pointer;font-weight:bold}
.btn-save{background:#00d9ff;color:#000}
.btn-cancel{background:#666;color:#fff}
.btn-delete-modal{background:#e94560;color:#fff}
.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 id="title">CRM H3R7</h1></header>
<div class="stats">
<div class="stat-card"><div class="stat-num" id="total">0</div><div class="stat-label">Total</div></div>
<div class="stat-card"><div class="stat-num" id="nouveaux">0</div><div class="stat-label">Nouveau</div></div>
<div class="stat-card"><div class="stat-num" id="qualifies">0</div><div class="stat-label">Qualifies</div></div>
<div class="stat-card"><div class="stat-num" id="gagnes">0</div><div class="stat-label">Gagnes</div></div>
</div>
<div class="filter-bar">
<button class="filter-btn active" onclick="filter('tous')">Tous</button>
<button class="filter-btn" onclick="filter('nouveau')">Nouveau</button>
<button class="filter-btn" onclick="filter('contacte')">Contacte</button>
<button class="filter-btn" onclick="filter('rdv')">RDV</button>
<button class="filter-btn" onclick="filter('proposition')">Proposition</button>
<button class="filter-btn" onclick="filter('gagne')">Gagne</button>
<button class="filter-btn" onclick="filter('perdu')">Perdu</button>
<select id="catFilter" onchange="render()">
<option value="all">Toutes categories</option>
<option value="Restaurant">Restaurants</option>
<option value="Boulangerie">Boulangeries</option>
<option value="Garage">Garages</option>
</select>
</div>
<div class="prospects-grid" id="grid"></div>
<div class="modal" id="modal">
<div class="modal-content">
<h2>Modifier le Prospect</h2>
<input type="hidden" id="eid">
<div class="form-group"><label>Nom</label><input type="text" id="enom"></div>
<div class="form-group"><label>Entreprise</label><input type="text" id="eentreprise"></div>
<div class="form-group"><label>Telephone</label><input type="tel" id="etel"></div>
<div class="form-group"><label>Statut</label><select id="estatut">
<option value="nouveau">Nouveau</option>
<option value="contacte">Contacte</option>
<option value="rdv">RDV</option>
<option value="proposition">Proposition</option>
<option value="gagne">Gagne</option>
<option value="perdu">Perdu</option>
</select></div>
<div class="form-group"><label>Date RDV</label><input type="date" id="erdv"></div>
<div class="form-group"><label>Commentaires</label><textarea id="enotes"></textarea></div>
<div class="modal-buttons">
<button class="btn-save" onclick="save()">Enregistrer</button>
<button class="btn-cancel" onclick="closeModal()">Annuler</button>
<button class="btn-delete-modal" onclick="delFromModal()">Supprimer</button>
</div>
</div>
</div>
<script>
var prospects = [];
var currentFilter = 'tous';
var currentCat = 'all';
fetch('api/prospects').then(r=>r.json()).then(function(data){
prospects = data.prospects;
document.getElementById('title').textContent = 'CRM H3R7 - ' + prospects.length + ' Prospects';
render();
});
function filter(f) {
currentFilter = f;
document.querySelectorAll('.filter-btn').forEach(function(b){b.classList.remove('active');});
event.target.classList.add('active');
render();
}
function render() {
currentCat = document.getElementById('catFilter').value;
var filtered = prospects;
if (currentCat !== 'all') {
filtered = filtered.filter(function(p){return p.categorie===currentCat || p.secteur===currentCat || p.entreprise===currentCat;});
}
if (currentFilter !== 'tous') {
filtered = filtered.filter(function(p){return p.statut===currentFilter;});
}
var grid = document.getElementById('grid');
if (!filtered.length) {
grid.innerHTML = '<div style="grid-column:1/-1;text-align:center;color:#666;padding:40px;">Aucun prospect</div>';
return;
}
grid.innerHTML = filtered.map(function(p) {
var rdv = p.rdv ? '<span style="background:#ff6b6b;color:#fff;padding:3px 8px;border-radius:10px;font-size:11px;margin-left:5px;">RDV: '+p.rdv+'</span>' : '';
var notes = p.notes ? '<div class="prospect-info">'+p.notes.substring(0,30)+'</div>' : '';
return '<div class="prospect-card '+(p.statut||'nouveau')+'">' +
'<div class="prospect-name">'+(p.nom||'')+'</div>' +
'<div class="prospect-entreprise">'+(p.entreprise||'')+'</div>' +
'<div class="prospect-info">'+(p.adresse||'-')+'</div>' +
'<div class="prospect-info">'+(p.tel||'-')+'</div>' +
'<div class="prospect-info">'+(p.score||0)+' ('+(p.categorie||'-')+')</div>' +
notes + rdv +
'<div class="actions">' +
'<button class="btn-edit" onclick="edit(\''+p.id+'\')">Modifier</button>' +
'<button class="btn-delete" onclick="del(\''+p.id+'\')">Supprimer</button>' +
'</div></div>';
}).join('');
updateStats();
}
function updateStats() {
document.getElementById('total').textContent = prospects.length;
document.getElementById('nouveaux').textContent = prospects.filter(function(p){return p.statut==='nouveau'}).length;
document.getElementById('qualifies').textContent = prospects.filter(function(p){return p.statut==='contacte' || p.statut==='rdv' || p.statut==='proposition'}).length;
document.getElementById('gagnes').textContent = prospects.filter(function(p){return p.statut==='gagne'}).length;
}
function edit(id) {
var p = prospects.find(function(x){return x.id===id;});
if (!p) return;
document.getElementById('eid').value = id;
document.getElementById('enom').value = p.nom || '';
document.getElementById('eentreprise').value = p.entreprise || '';
document.getElementById('etel').value = p.tel || '';
document.getElementById('estatut').value = p.statut || 'nouveau';
document.getElementById('erdv').value = p.rdv || '';
document.getElementById('enotes').value = p.notes || '';
document.getElementById('modal').classList.add('show');
}
function closeModal() {
document.getElementById('modal').classList.remove('show');
}
function save() {
var id = document.getElementById('eid').value;
var p = prospects.find(function(x){return x.id===id;});
if (!p) return;
p.nom = document.getElementById('enom').value;
p.entreprise = document.getElementById('eentreprise').value;
p.tel = document.getElementById('etel').value;
p.statut = document.getElementById('estatut').value;
p.rdv = document.getElementById('erdv').value;
p.notes = document.getElementById('enotes').value;
fetch('api/prospect/'+id, {
method: 'PUT',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(p)
}).then(function(){closeModal(); render();});
}
function del(id) {
if (!confirm('Supprimer ce prospect?')) return;
fetch('api/prospect/'+id, {method: 'DELETE'}).then(function(){
prospects = prospects.filter(function(p){return p.id!==id;});
render();
});
}
function delFromModal() {
var id = document.getElementById('eid').value;
del(id);
closeModal();
}
</script>
</body>
</html>