323 lines
16 KiB
HTML
323 lines
16 KiB
HTML
{% extends 'layouts/base.html' %}
|
|
|
|
{% block title %}Suite Consultance - Campagne d'email{% endblock %}
|
|
{% block module_name %}crm{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h1 class="h2 text-crm">Campagne d'email aux prospects</h1>
|
|
<a href="{{ url_for('crm') }}" class="btn btn-outline-crm">
|
|
<i class="fas fa-arrow-left"></i> Retour au CRM
|
|
</a>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<form method="POST" action="{{ url_for('send_bulk_email') }}">
|
|
<div class="row g-3">
|
|
<!-- Sélection des prospects -->
|
|
<div class="col-12">
|
|
<h5 class="border-bottom pb-2 mb-3">Sélection des prospects</h5>
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">Filtre par statut</label>
|
|
<div class="d-flex flex-wrap gap-2">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="status_all" name="status_all" checked>
|
|
<label class="form-check-label" for="status_all">
|
|
Tous
|
|
</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input status-option" type="checkbox" id="status_nouveau" name="status[]" value="Nouveau">
|
|
<label class="form-check-label" for="status_nouveau">
|
|
Nouveau
|
|
</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input status-option" type="checkbox" id="status_contacte" name="status[]" value="Contacté">
|
|
<label class="form-check-label" for="status_contacte">
|
|
Contacté
|
|
</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input status-option" type="checkbox" id="status_relance" name="status[]" value="Relancé">
|
|
<label class="form-check-label" for="status_relance">
|
|
Relancé
|
|
</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input status-option" type="checkbox" id="status_qualifie" name="status[]" value="Qualifié">
|
|
<label class="form-check-label" for="status_qualifie">
|
|
Qualifié
|
|
</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input status-option" type="checkbox" id="status_proposition" name="status[]" value="Proposition">
|
|
<label class="form-check-label" for="status_proposition">
|
|
Proposition
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">Filtre par tags</label>
|
|
<input type="text" class="form-control" id="tags_filter" name="tags" placeholder="ex: web, urgent, pme">
|
|
<small class="text-muted">Séparer les tags par des virgules</small>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-12">
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th width="50">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="select_all_prospects" checked>
|
|
</div>
|
|
</th>
|
|
<th>Nom</th>
|
|
<th>Email</th>
|
|
<th>Statut</th>
|
|
<th>Tags</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="prospects_table">
|
|
{% for prospect in prospects %}
|
|
{# Convertit le statut en chaîne si enum #}
|
|
{% set s = prospect.status %}
|
|
{% if not (s is string) %}{% set s = s.value %}{% endif %}
|
|
<tr class="prospect-row" data-status="{{ s }}" data-tags="{{ prospect.tags|join(',') }}">
|
|
<td>
|
|
<div class="form-check">
|
|
<input class="form-check-input prospect-select" type="checkbox" name="prospect_ids[]" value="{{ prospect.id }}" checked>
|
|
</div>
|
|
</td>
|
|
<td>{{ prospect.name }}</td>
|
|
<td>{{ prospect.email }}</td>
|
|
<td>
|
|
{# Mapping badge #}
|
|
{% set cls = 'secondary' %}
|
|
{% if s|lower == 'nouveau' %}
|
|
{% set cls = 'primary' %}
|
|
{% elif s|lower == 'contacté' or s|lower == 'contacte' %}
|
|
{% set cls = 'info' %}
|
|
{% elif s|lower == 'qualifié' or s|lower == 'qualifie' %}
|
|
{% set cls = 'success' %}
|
|
{% elif s|lower == 'proposition' %}
|
|
{% set cls = 'warning' %}
|
|
{% endif %}
|
|
<span class="badge bg-{{ cls }}">{{ s }}</span>
|
|
</td>
|
|
<td>
|
|
{% for tag in prospect.tags %}
|
|
<span class="badge bg-secondary">{{ tag }}</span>
|
|
{% endfor %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Contenu de l'email -->
|
|
<div class="col-12">
|
|
<h5 class="border-bottom pb-2 mb-3 mt-3">Contenu de l'email</h5>
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="template_id" class="form-label">Template</label>
|
|
<select class="form-select" id="template_id" name="template_id">
|
|
<option value="">Email personnalisé</option>
|
|
{% for template in templates %}
|
|
<option value="{{ template.id }}">{{ template.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-12">
|
|
<div class="mb-3">
|
|
<label for="subject" class="form-label">Sujet *</label>
|
|
<input type="text" class="form-control" id="subject" name="subject" required>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-12">
|
|
<div class="mb-3">
|
|
<label for="body" class="form-label">Contenu *</label>
|
|
<textarea class="form-control" id="body" name="body" rows="10" required></textarea>
|
|
<small class="text-muted">Variables disponibles: {{nom}}, {{entreprise}}, {{email}}. Vous pouvez utiliser du texte formaté HTML.</small>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-12">
|
|
<div class="mb-3">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="update_status" name="update_status" checked>
|
|
<label class="form-check-label" for="update_status">
|
|
Mettre à jour le statut des prospects
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-6" id="status_selection">
|
|
<div class="mb-3">
|
|
<label for="new_status" class="form-label">Nouveau statut</label>
|
|
<select class="form-select" id="new_status" name="new_status">
|
|
<option value="Contacté">Contacté</option>
|
|
<option value="Relancé">Relancé</option>
|
|
<option value="Qualifié">Qualifié</option>
|
|
<option value="Proposition">Proposition</option>
|
|
<option value="Non intéressé">Non intéressé</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-12 mt-3">
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fas fa-paper-plane"></i> Envoyer la campagne
|
|
</button>
|
|
<a href="{{ url_for('crm') }}" class="btn btn-outline-secondary">
|
|
Annuler
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const templateSelect = document.getElementById('template_id');
|
|
const subjectInput = document.getElementById('subject');
|
|
const bodyInput = document.getElementById('body');
|
|
const updateStatusCheckbox = document.getElementById('update_status');
|
|
const statusSelection = document.getElementById('status_selection');
|
|
const selectAllProspects = document.getElementById('select_all_prospects');
|
|
const statusAllCheckbox = document.getElementById('status_all');
|
|
const statusOptions = document.querySelectorAll('.status-option');
|
|
const prospectRows = document.querySelectorAll('.prospect-row');
|
|
const tagsFilter = document.getElementById('tags_filter');
|
|
|
|
// Gestion du choix de template
|
|
templateSelect.addEventListener('change', function() {
|
|
const templateId = this.value;
|
|
if (templateId) {
|
|
// Charger le contenu du template
|
|
fetch(`/api/email_template/${templateId}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
subjectInput.value = data.template.subject;
|
|
bodyInput.value = data.template.content;
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// Afficher/masquer le sélecteur de statut
|
|
updateStatusCheckbox.addEventListener('change', function() {
|
|
statusSelection.style.display = this.checked ? 'block' : 'none';
|
|
});
|
|
|
|
// Initialiser l'affichage du sélecteur de statut
|
|
statusSelection.style.display = updateStatusCheckbox.checked ? 'block' : 'none';
|
|
|
|
// Sélectionner/désélectionner tous les prospects
|
|
selectAllProspects.addEventListener('change', function() {
|
|
document.querySelectorAll('.prospect-select:not(:disabled)').forEach(checkbox => {
|
|
checkbox.checked = this.checked;
|
|
});
|
|
});
|
|
|
|
// Gestion des filtres par statut
|
|
statusAllCheckbox.addEventListener('change', function() {
|
|
if (this.checked) {
|
|
statusOptions.forEach(option => {
|
|
option.checked = false;
|
|
option.disabled = true;
|
|
});
|
|
|
|
// Afficher tous les prospects
|
|
prospectRows.forEach(row => {
|
|
row.style.display = '';
|
|
row.querySelector('.prospect-select').disabled = false;
|
|
});
|
|
} else {
|
|
statusOptions.forEach(option => {
|
|
option.disabled = false;
|
|
});
|
|
|
|
// Appliquer les filtres
|
|
applyFilters();
|
|
}
|
|
});
|
|
|
|
// Gestion des options de statut individuelles
|
|
statusOptions.forEach(option => {
|
|
option.addEventListener('change', function() {
|
|
applyFilters();
|
|
});
|
|
});
|
|
|
|
// Gestion du filtre par tags
|
|
tagsFilter.addEventListener('input', function() {
|
|
applyFilters();
|
|
});
|
|
|
|
// Fonction pour appliquer les filtres
|
|
function applyFilters() {
|
|
// Désactiver "Tous" si une option spécifique est choisie
|
|
if (Array.from(statusOptions).some(opt => opt.checked)) {
|
|
statusAllCheckbox.checked = false;
|
|
}
|
|
|
|
// Récupérer les statuts sélectionnés
|
|
const selectedStatuses = Array.from(statusOptions)
|
|
.filter(option => option.checked)
|
|
.map(option => option.value);
|
|
|
|
// Récupérer les tags saisis
|
|
const tags = tagsFilter.value.toLowerCase().split(',').map(tag => tag.trim()).filter(tag => tag);
|
|
|
|
// Appliquer les filtres aux lignes
|
|
prospectRows.forEach(row => {
|
|
const rowStatus = row.getAttribute('data-status');
|
|
const rowTags = row.getAttribute('data-tags').toLowerCase().split(',');
|
|
|
|
let statusMatch = selectedStatuses.length === 0 || selectedStatuses.includes(rowStatus);
|
|
let tagsMatch = tags.length === 0 || tags.some(tag => rowTags.includes(tag));
|
|
|
|
if (statusAllCheckbox.checked) {
|
|
statusMatch = true;
|
|
}
|
|
|
|
if (statusMatch && tagsMatch) {
|
|
row.style.display = '';
|
|
row.querySelector('.prospect-select').disabled = false;
|
|
} else {
|
|
row.style.display = 'none';
|
|
row.querySelector('.prospect-select').disabled = true;
|
|
row.querySelector('.prospect-select').checked = false;
|
|
}
|
|
});
|
|
|
|
// Mettre à jour "Sélectionner tout" si aucun prospect n'est visible
|
|
const visibleProspects = document.querySelectorAll('.prospect-row[style=""]');
|
|
selectAllProspects.disabled = visibleProspects.length === 0;
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %}
|