first commit

This commit is contained in:
mrtoine 2025-09-20 13:18:04 +02:00
commit e6c52820cd
227 changed files with 16156 additions and 0 deletions

View file

@ -0,0 +1,54 @@
<div class="modal fade" id="createTaskModal" tabindex="-1" aria-labelledby="createTaskModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<form method="post" action="{{ url_for('tasks.create_task') }}" class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="createTaskModalLabel"><i class="fas fa-plus me-2"></i>Nouvelle tâche</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Fermer"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Titre *</label>
<input type="text" name="title" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">Échéance *</label>
<input type="date" name="due_date" class="form-control" value="{{ today if today is defined else '' }}" required>
</div>
<div class="mb-3">
<label class="form-label">Description</label>
<textarea name="description" class="form-control" rows="3"></textarea>
</div>
<div class="row">
<div class="mb-3 col-md-6">
<label class="form-label">Priorité</label>
<select name="priority" class="form-select">
<option value="basse">Basse</option>
<option value="normale" selected>Normale</option>
<option value="haute">Haute</option>
</select>
</div>
<div class="mb-3 col-md-6">
<label class="form-label">Type dentité</label>
<select name="entity_type" class="form-select">
<option value="">Aucune</option>
<option value="client">Client</option>
<option value="prospect">Prospect</option>
<option value="project">Projet</option>
<option value="campaign">Campagne</option>
</select>
</div>
</div>
<div class="mb-3">
<label class="form-label">ID de lentité (optionnel)</label>
<input type="text" name="entity_id" class="form-control" placeholder="cli_xxx / pros_xxx / id projet...">
</div>
</div>
<div class="modal-footer">
<a href="{{ url_for('tasks.tasks_index') }}" class="btn btn-outline-secondary">Gérer les tâches</a>
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1"></i>Créer
</button>
</div>
</form>
</div>
</div>

View file

@ -0,0 +1,14 @@
{# Bouton réutilisable pour créer une tâche #}
{# Il lie automatiquement si entity_type et entity_id sont disponibles dans le contexte #}
{% set et = (entity_type | default(request.args.get('entity_type'))) %}
{% set eid = (entity_id | default(request.args.get('entity_id'))) %}
{% if et and eid %}
<a class="btn btn-outline-success" href="{{ url_for('tasks.quick_add_page', entity_type=et, entity_id=eid) }}">
Ajouter une tâche
</a>
{% else %}
<a class="btn btn-outline-secondary" href="{{ url_for('tasks.tasks_index') }}">
Nouvelle tâche
</a>
{% endif %}

View file

@ -0,0 +1,31 @@
<form method="post" action="{{ url_for('tasks.create_task') }}">
<input type="hidden" name="entity_type" value="{{ entity_type }}">
<input type="hidden" name="entity_id" value="{{ entity_id }}">
<div class="mb-3">
<label class="form-label">Titre</label>
<input type="text" class="form-control" name="title" placeholder="Ex: Relancer le prospect" required>
</div>
<div class="row">
<div class="col-md-4 mb-3">
<label class="form-label">Échéance</label>
<input type="date" class="form-control" name="due_date" value="{{ today }}" required>
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Priorité</label>
<select class="form-select" name="priority">
<option value="basse">Basse</option>
<option value="normale" selected>Normale</option>
<option value="haute">Haute</option>
</select>
</div>
</div>
<div class="mb-3">
<label class="form-label">Description</label>
<textarea class="form-control" name="description" rows="3" placeholder="Détails éventuels..."></textarea>
</div>
<button type="submit" class="btn btn-primary">Créer la tâche</button>
</form>

View file

@ -0,0 +1,23 @@
{}
{# Partial: affiche une table de tâches à partir de la variable `tasks` #}
{% import "tasks/macros.html" as tsk %}
<div style="border:1px solid #e5e5e5;border-radius:.5rem;padding:1rem;">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:.6rem;">
<h3 style="margin:0;font-size:1.1rem;">Tâches</h3>
<div style="font-size:.9rem;color:#666;">
Total: {{ tasks|length if tasks else 0 }}
</div>
</div>
{{ tsk.render_tasks_table(tasks, show_entity=True, show_due_date=True) }}
</div>
{# Partial: affiche une table de tâches à partir de la variable `tasks` #}
{% import "tasks/macros.html" as tsk %}
<div style="border:1px solid #e5e5e5;border-radius:.5rem;padding:1rem;">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:.6rem;">
<h3 style="margin:0;font-size:1.1rem;">Tâches</h3>
<div style="font-size:.9rem;color:#666;">
Total: {{ tasks|length if tasks else 0 }}
</div>
</div>
{{ tsk.render_tasks_table(tasks, show_entity=True, show_due_date=True) }}
</div>

View file

@ -0,0 +1,74 @@
{# Sidebar offcanvas gauche pour afficher les tâches du jour #}
<div class="offcanvas offcanvas-start" tabindex="-1" id="tasksTodaySidebar" aria-labelledby="tasksTodaySidebarLabel" style="width:560px;">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tasksTodaySidebarLabel">
<i class="fas fa-list-check me-2"></i>Tâches
</h5>
<a href="{{ url_for('tasks.tasks_index') }}" class="btn btn-warning btn-sm"><i class="fas fa-cog me-1"></i></a>
<a href="{{ url_for('tasks.email_drafts_list') }}" class="btn btn-success btn-sm"><i class="fa-solid fa-file-arrow-up"></i></a>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Fermer"></button>
</div>
<div class="offcanvas-body">
<ul class="nav nav-tabs mb-3">
<li class="nav-item">
<a class="nav-link active" id="today-tab" data-bs-toggle="tab" href="#today">Aujourd'hui</a>
</li>
<li class="nav-item">
<a class="nav-link" id="tomorrow-tab" data-bs-toggle="tab" href="#tomorrow">Demain</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade show active" id="today">
<div id="tasksTodayContent">
<div class="placeholder">Chargement des tâches...</div>
</div>
</div>
<div class="tab-pane fade" id="tomorrow">
<div id="tasksTomorrowContent">
<div class="placeholder">Chargement des tâches...</div>
</div>
</div>
</div>
</div>
</div>
<script>
(function(){
const todayContainer = document.getElementById('tasksTodayContent');
const tomorrowContainer = document.getElementById('tasksTomorrowContent');
async function loadTasks(container, url) {
try {
const res = await fetch(url, {headers: {'X-Requested-With': 'XMLHttpRequest'}});
if (!res.ok) throw new Error('HTTP ' + res.status);
container.innerHTML = await res.text();
} catch (e) {
container.innerHTML = '<div class="text-muted">Impossible de charger les tâches.</div>';
}
}
async function loadTodayTasks() {
await loadTasks(todayContainer, '{{ url_for("tasks.today_fragment") }}');
}
async function loadTomorrowTasks() {
await loadTasks(tomorrowContainer, '{{ url_for("tasks.tomorrow_fragment") }}');
}
// Charger au premier affichage de la page
document.addEventListener('DOMContentLoaded', loadTodayTasks);
// Recharger à l'ouverture de l'offcanvas
const sidebar = document.getElementById('tasksTodaySidebar');
if (sidebar) {
sidebar.addEventListener('show.bs.offcanvas', () => {
loadTodayTasks();
loadTomorrowTasks();
});
}
// Gérer les changements d'onglets
const tomorrowTab = document.getElementById('tomorrow-tab');
tomorrowTab.addEventListener('shown.bs.tab', loadTomorrowTasks);
})();
</script>

View file

@ -0,0 +1,11 @@
{# Partial: bloc des tâches du jour, attend `today_tasks` dans le contexte #}
{% import "tasks/macros.html" as tsk %}
<div style="border:1px solid #e5e5e5;border-radius:.5rem;padding:1rem;background:#fafafa;">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:.6rem;">
<h3 style="margin:0;font-size:1.1rem;">Tâches du jour</h3>
<div style="font-size:.9rem;color:#666;">
{{ today_tasks|length if today_tasks else 0 }} tâche(s)
</div>
</div>
{{ tsk.render_tasks_table(today_tasks, show_entity=True, show_due_date=False, empty_text="Aucune tâche pour aujourd'hui") }}
</div>