first commit
This commit is contained in:
commit
e6c52820cd
227 changed files with 16156 additions and 0 deletions
45
Templates/tasks/email_drafts.html
Normal file
45
Templates/tasks/email_drafts.html
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
{% extends "layouts/base.html" %}
|
||||
|
||||
{% block title %}Brouillons d'emails{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h2 class="mb-0">Brouillons d'emails à envoyer</h2>
|
||||
<div class="d-flex gap-2">
|
||||
<form method="post" action="{{ url_for('tasks.email_drafts_generate') }}">
|
||||
<button type="submit" class="btn btn-primary">Générer pour aujourd'hui</button>
|
||||
</form>
|
||||
{% include "partials/task_quick_add_button.html" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if drafts %}
|
||||
{% for d in drafts %}
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<h5 class="card-title mb-0">{{ d.subject }}</h5>
|
||||
<form method="post" action="{{ url_for('tasks.email_drafts_send') }}">
|
||||
<input type="hidden" name="draft_id" value="{{ d.id }}">
|
||||
<button type="submit" class="btn btn-success">Envoyer</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<p class="text-muted mt-2 mb-2">À: {{ d.to_email }}</p>
|
||||
<div class="border rounded p-3 mb-2 bg-light">
|
||||
{{ d.content | safe }}
|
||||
</div>
|
||||
<small class="text-muted">Prospect: {{ d.prospect_id }} | Template: {{ d.template_id or '-' }}</small>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p>Aucun brouillon à envoyer.</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-3">
|
||||
<a href="{{ url_for('tasks.tasks_index') }}">← Retour aux tâches</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
10
Templates/tasks/list.html
Normal file
10
Templates/tasks/list.html
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{% import "tasks/macros.html" as tsk %}
|
||||
<div style="max-width:1100px;margin:0 auto;padding:1rem;">
|
||||
<h2 style="margin-top:0;">Liste des tâches</h2>
|
||||
{{ tsk.render_tasks_table(tasks, show_entity=True, show_due_date=True) }}
|
||||
</div>
|
||||
{% import "tasks/macros.html" as tsk %}
|
||||
<div style="max-width:1100px;margin:0 auto;padding:1rem;">
|
||||
<h2 style="margin-top:0;">Liste des tâches</h2>
|
||||
{{ tsk.render_tasks_table(tasks, show_entity=True, show_due_date=True) }}
|
||||
</div>
|
||||
413
Templates/tasks/macros.html
Normal file
413
Templates/tasks/macros.html
Normal file
|
|
@ -0,0 +1,413 @@
|
|||
{# Macros de rendu pour les tâches #}
|
||||
{% macro priority_badge(priority) -%}
|
||||
{% set classes = {
|
||||
'haute': 'background:#ffe5e5;color:#b30000;padding:.15rem .4rem;border-radius:.3rem;font-size:.8rem;',
|
||||
'normale': 'background:#e8f1ff;color:#003a8c;padding:.15rem .4rem;border-radius:.3rem;font-size:.8rem;',
|
||||
'basse': 'background:#e9f7ef;color:#1b5e20;padding:.15rem .4rem;border-radius:.3rem;font-size:.8rem;'
|
||||
} %}
|
||||
<span style="{{ classes.get(priority, classes['normale']) }}">{{ priority|capitalize }}</span>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro status_badge(status) -%}
|
||||
{% set classes = {
|
||||
'todo': 'background:#fff3cd;color:#8a6d3b;padding:.15rem .4rem;border-radius:.3rem;font-size:.8rem;',
|
||||
'done': 'background:#e6ffed;color:#155724;padding:.15rem .4rem;border-radius:.3rem;font-size:.8rem;',
|
||||
'canceled': 'background:#f8d7da;color:#721c24;padding:.15rem .4rem;border-radius:.3rem;font-size:.8rem;'
|
||||
} %}
|
||||
{% set labels = {'todo': 'À faire', 'done': 'Fait', 'canceled': 'Annulée'} %}
|
||||
<span style="{{ classes.get(status, classes['todo']) }}">{{ labels.get(status, status) }}</span>
|
||||
{%- endmacro %}
|
||||
{# Macros de rendu pour les tâches #}
|
||||
{% macro priority_badge(priority) -%}
|
||||
{% set classes = {
|
||||
'haute': 'background:#ffe5e5;color:#b30000;padding:.15rem .4rem;border-radius:.3rem;font-size:.8rem;',
|
||||
'normale': 'background:#e8f1ff;color:#003a8c;padding:.15rem .4rem;border-radius:.3rem;font-size:.8rem;',
|
||||
'basse': 'background:#e9f7ef;color:#1b5e20;padding:.15rem .4rem;border-radius:.3rem;font-size:.8rem;'
|
||||
} %}
|
||||
<span style="{{ classes.get(priority, classes['normale']) }}">{{ priority|capitalize }}</span>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro status_badge(status) -%}
|
||||
{% set classes = {
|
||||
'todo': 'background:#fff3cd;color:#8a6d3b;padding:.15rem .4rem;border-radius:.3rem;font-size:.8rem;',
|
||||
'done': 'background:#e6ffed;color:#155724;padding:.15rem .4rem;border-radius:.3rem;font-size:.8rem;',
|
||||
'canceled': 'background:#f8d7da;color:#721c24;padding:.15rem .4rem;border-radius:.3rem;font-size:.8rem;'
|
||||
} %}
|
||||
{% set labels = {'todo': 'À faire', 'done': 'Fait', 'canceled': 'Annulée'} %}
|
||||
<span style="{{ classes.get(status, classes['todo']) }}">{{ labels.get(status, status) }}</span>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro entity_badge(entity_type) -%}
|
||||
{% if entity_type %}
|
||||
{% set labels = {'client': 'Client', 'prospect': 'Prospect', 'project': 'Projet', 'campaign': 'Campagne'} %}
|
||||
<span style="background:#f0f0f0;color:#333;padding:.15rem .4rem;border-radius:.3rem;font-size:.75rem;">
|
||||
{{ labels.get(entity_type, entity_type|capitalize) }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro render_task_row(task, show_entity=True, show_due_date=True) -%}
|
||||
<tr style="border-bottom:1px solid #eee;">
|
||||
<td style="white-space:nowrap;padding:.5rem .4rem;">
|
||||
{{ status_badge(task.status) }}
|
||||
</td>
|
||||
<td style="padding:.5rem .4rem;">
|
||||
<div style="font-weight:600;">{{ task.title }}</div>
|
||||
{% if task.description %}
|
||||
<div style="font-size:.9rem;color:#666;">{{ task.description }}</div>
|
||||
{% endif %}
|
||||
{% if task.metadata and task.metadata.ref %}
|
||||
<div style="font-size:.85rem;color:#999;">Ref: {{ task.metadata.ref }}</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if show_entity %}
|
||||
<td style="white-space:nowrap;padding:.5rem .4rem;">
|
||||
{{ entity_badge(task.entity_type) }}
|
||||
{% if task.entity_id %}
|
||||
<small style="color:#888;display:block;">{{ task.entity_id }}</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
{% if show_due_date %}
|
||||
<td style="white-space:nowrap;padding:.5rem .4rem;">
|
||||
<span style="color:#333;">{{ task.due_date }}</span>
|
||||
{% if task.completed_at and task.status == 'done' %}
|
||||
<div style="font-size:.8rem;color:#6c757d;">Clôturée: {{ task.completed_at }}</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<td style="white-space:nowrap;text-align:right;padding:.5rem .4rem;">
|
||||
{{ priority_badge(task.priority) }}
|
||||
</td>
|
||||
</tr>
|
||||
{%- endmacro %}
|
||||
{# Macros de rendu pour les tâches #}
|
||||
{% macro priority_badge(priority) -%}
|
||||
{% set cls = {'haute':'bg-danger','normale':'bg-primary','basse':'bg-success'} %}
|
||||
<span class="badge {{ cls.get(priority, 'bg-primary') }}">{{ priority|capitalize }}</span>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro status_badge(status) -%}
|
||||
{% set cls = {'todo':'bg-warning text-dark','done':'bg-success','canceled':'bg-danger'} %}
|
||||
{% set labels = {'todo': 'À faire', 'done': 'Fait', 'canceled': 'Annulée'} %}
|
||||
<span class="badge {{ cls.get(status, 'bg-secondary') }}">{{ labels.get(status, status) }}</span>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro entity_badge(entity_type) -%}
|
||||
{% if entity_type %}
|
||||
{% set labels = {'client': 'Client', 'prospect': 'Prospect', 'project': 'Projet', 'campaign': 'Campagne'} %}
|
||||
<span class="badge bg-secondary-subtle text-dark">{{ labels.get(entity_type, entity_type|capitalize) }}</span>
|
||||
{% endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro render_task_row(task, show_entity=True, show_due_date=True, show_actions=False) -%}
|
||||
<tr>
|
||||
<td class="text-nowrap">
|
||||
{{ status_badge(task.status) }}
|
||||
</td>
|
||||
<td>
|
||||
<div class="fw-semibold">{{ task.title }}</div>
|
||||
{% if task.description %}
|
||||
<div class="small text-muted">{{ task.description }}</div>
|
||||
{% endif %}
|
||||
{% if task.metadata and task.metadata.ref %}
|
||||
<div class="small text-secondary">Ref: {{ task.metadata.ref }}</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if show_entity %}
|
||||
<td class="text-nowrap">
|
||||
{{ entity_badge(task.entity_type) }}
|
||||
{% if task.entity_id %}
|
||||
<div class="small text-muted">{{ task.entity_id }}</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
{% if show_due_date %}
|
||||
<td class="text-nowrap">
|
||||
<span>{{ task.due_date }}</span>
|
||||
{% if task.completed_at and task.status == 'done' %}
|
||||
<div class="small text-muted">Clôturée: {{ task.completed_at }}</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<td class="text-nowrap text-end">
|
||||
{{ priority_badge(task.priority) }}
|
||||
</td>
|
||||
{% if show_actions %}
|
||||
<td class="text-nowrap text-end">
|
||||
<div class="d-flex gap-1 justify-content-end">
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-outline-secondary btn-edit-task"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#editTaskModal"
|
||||
data-id="{{ task.id }}"
|
||||
data-title="{{ task.title }}"
|
||||
data-due_date="{{ task.due_date }}"
|
||||
data-priority="{{ task.priority }}"
|
||||
data-description="{{ task.description or '' }}"
|
||||
data-entity_type="{{ task.entity_type or '' }}"
|
||||
data-entity_id="{{ task.entity_id or '' }}"
|
||||
title="Éditer">
|
||||
<i class="fas fa-pen"></i>
|
||||
</button>
|
||||
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-sm btn-outline-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false" title="Changer le statut">
|
||||
<i class="fas fa-exchange-alt"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li>
|
||||
<form method="post" action="{{ url_for('tasks.set_status') }}">
|
||||
<input type="hidden" name="task_id" value="{{ task.id }}">
|
||||
<input type="hidden" name="status" value="todo">
|
||||
<button type="submit" class="dropdown-item {% if task.status=='todo' %}active{% endif %}">À faire</button>
|
||||
</form>
|
||||
</li>
|
||||
<li>
|
||||
<form method="post" action="{{ url_for('tasks.set_status') }}">
|
||||
<input type="hidden" name="task_id" value="{{ task.id }}">
|
||||
<input type="hidden" name="status" value="done">
|
||||
<button type="submit" class="dropdown-item {% if task.status=='done' %}active{% endif %}">Fait</button>
|
||||
</form>
|
||||
</li>
|
||||
<li>
|
||||
<form method="post" action="{{ url_for('tasks.set_status') }}">
|
||||
<input type="hidden" name="task_id" value="{{ task.id }}">
|
||||
<input type="hidden" name="status" value="canceled">
|
||||
<button type="submit" class="dropdown-item {% if task.status=='canceled' %}active{% endif %}">Annulée</button>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<form method="post" action="{{ url_for('tasks.delete_task') }}" onsubmit="return confirm('Supprimer cette tâche ?');">
|
||||
<input type="hidden" name="task_id" value="{{ task.id }}">
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger" title="Supprimer">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro render_tasks_table(tasks, show_entity=True, show_due_date=True, empty_text="Aucune tâche", show_actions=False, today=None) -%}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
{% set cols = 3 + (1 if show_entity else 0) + (1 if show_due_date else 0) + (1 if show_actions else 0) %}
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Statut</th>
|
||||
<th>Tâche</th>
|
||||
{% if show_entity %}<th>Liée à</th>{% endif %}
|
||||
{% if show_due_date %}<th>Échéance</th>{% endif %}
|
||||
<th class="text-end">Priorité</th>
|
||||
{% if show_actions %}<th class="text-end">Actions</th>{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if tasks and tasks|length > 0 %}
|
||||
{% for task in tasks %}
|
||||
{{ render_task_row(task, show_entity=show_entity, show_due_date=show_due_date, show_actions=show_actions) }}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="{{ cols }}" class="text-muted"> {{ empty_text }} </td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
{% macro render_tasks_table(tasks, show_entity=True, show_due_date=True, empty_text="Aucune tâche", show_actions=False, today=None) -%}
|
||||
<div style="overflow:auto;">
|
||||
<table style="width:100%;border-collapse:collapse;">
|
||||
{% set cols = 3 + (1 if show_entity else 0) + (1 if show_due_date else 0) + (1 if show_actions else 0) %}
|
||||
<thead>
|
||||
<tr style="text-align:left;border-bottom:1px solid #e5e5e5;">
|
||||
<th style="padding:.6rem .4rem;">Statut</th>
|
||||
<th style="padding:.6rem .4rem;">Tâche</th>
|
||||
{% if show_entity %}<th style="padding:.6rem .4rem;">Liée à</th>{% endif %}
|
||||
{% if show_due_date %}<th style="padding:.6rem .4rem;">Échéance</th>{% endif %}
|
||||
<th style="padding:.6rem .4rem;text-align:right;">Priorité</th>
|
||||
{% if show_actions %}<th style="padding:.6rem .4rem;text-align:right;">Actions</th>{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if tasks and tasks|length > 0 %}
|
||||
{% for task in tasks %}
|
||||
<tr style="border-bottom:1px solid #e5e5e5;">
|
||||
<td style="white-space:nowrap;padding:.5rem .4rem;">
|
||||
{{ status_badge(task.status) }}
|
||||
</td>
|
||||
<td style="padding:.5rem .4rem;">
|
||||
<div style="font-weight:600;">{{ task.title }}</div>
|
||||
{% if task.description %}
|
||||
<div style="font-size:.9rem;color:#666;">{{ task.description }}</div>
|
||||
{% endif %}
|
||||
{% if task.metadata and task.metadata.ref %}
|
||||
<div style="font-size:.85rem;color:#999;">Ref: {{ task.metadata.ref }}</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if show_entity %}
|
||||
<td style="white-space:nowrap;padding:.5rem .4rem;">
|
||||
{{ entity_badge(task.entity_type) }}
|
||||
{% if task.entity_id %}
|
||||
<small style="color:#888;display:block;">{{ task.entity_id }}</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
{% if show_due_date %}
|
||||
<td style="white-space:nowrap;padding:.5rem .4rem;">
|
||||
<span style="color:#333;">{{ task.due_date }}</span>
|
||||
{% if task.completed_at and task.status == 'done' %}
|
||||
<div style="font-size:.8rem;color:#6c757d;">Clôturée: {{ task.completed_at }}</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<td style="white-space:nowrap;text-align:right;padding:.5rem .4rem;">
|
||||
{{ priority_badge(task.priority) }}
|
||||
</td>
|
||||
{% if show_actions %}
|
||||
<td style="white-space:nowrap;text-align:right;padding:.5rem .4rem;">
|
||||
<div style="display:flex;gap:.35rem;justify-content:flex-end;">
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-outline-secondary btn-edit-task"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#editTaskModal"
|
||||
data-id="{{ task.id }}"
|
||||
data-title="{{ task.title }}"
|
||||
data-due_date="{{ task.due_date }}"
|
||||
data-priority="{{ task.priority }}"
|
||||
data-description="{{ task.description or '' }}"
|
||||
data-entity_type="{{ task.entity_type or '' }}"
|
||||
data-entity_id="{{ task.entity_id or '' }}"
|
||||
title="Éditer">
|
||||
<i class="fas fa-pen"></i>
|
||||
</button>
|
||||
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-sm btn-outline-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false" title="Changer le statut">
|
||||
<i class="fas fa-exchange-alt"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li>
|
||||
<form method="post" action="{{ url_for('tasks.set_status') }}">
|
||||
<input type="hidden" name="task_id" value="{{ task.id }}">
|
||||
<input type="hidden" name="status" value="todo">
|
||||
<button type="submit" class="dropdown-item {% if task.status=='todo' %}active{% endif %}">À faire</button>
|
||||
</form>
|
||||
</li>
|
||||
<li>
|
||||
<form method="post" action="{{ url_for('tasks.set_status') }}">
|
||||
<input type="hidden" name="task_id" value="{{ task.id }}">
|
||||
<input type="hidden" name="status" value="done">
|
||||
<button type="submit" class="dropdown-item {% if task.status=='done' %}active{% endif %}">Fait</button>
|
||||
</form>
|
||||
</li>
|
||||
<li>
|
||||
<form method="post" action="{{ url_for('tasks.set_status') }}">
|
||||
<input type="hidden" name="task_id" value="{{ task.id }}">
|
||||
<input type="hidden" name="status" value="canceled">
|
||||
<button type="submit" class="dropdown-item {% if task.status=='canceled' %}active{% endif %}">Annulée</button>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<form method="post" action="{{ url_for('tasks.delete_task') }}" onsubmit="return confirm('Supprimer cette tâche ?');">
|
||||
<input type="hidden" name="task_id" value="{{ task.id }}">
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger" title="Supprimer">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="{{ cols }}" style="padding:.8rem;color:#777;">
|
||||
{{ empty_text }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
{% macro entity_badge(entity_type) -%}
|
||||
{% if entity_type %}
|
||||
{% set labels = {'client': 'Client', 'prospect': 'Prospect', 'project': 'Projet', 'campaign': 'Campagne'} %}
|
||||
<span style="background:#f0f0f0;color:#333;padding:.15rem .4rem;border-radius:.3rem;font-size:.75rem;">
|
||||
{{ labels.get(entity_type, entity_type|capitalize) }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro render_task_row(task, show_entity=True, show_due_date=True) -%}
|
||||
<tr>
|
||||
<td style="white-space:nowrap;">
|
||||
{{ status_badge(task.status) }}
|
||||
</td>
|
||||
<td>
|
||||
<div style="font-weight:600;">{{ task.title }}</div>
|
||||
{% if task.description %}
|
||||
<div style="font-size:.9rem;color:#666;">{{ task.description }}</div>
|
||||
{% endif %}
|
||||
{% if task.metadata and task.metadata.ref %}
|
||||
<div style="font-size:.85rem;color:#999;">Ref: {{ task.metadata.ref }}</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if show_entity %}
|
||||
<td style="white-space:nowrap;">
|
||||
{{ entity_badge(task.entity_type) }}
|
||||
{% if task.entity_id %}
|
||||
<small style="color:#888;display:block;">{{ task.entity_id }}</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
{% if show_due_date %}
|
||||
<td style="white-space:nowrap;">
|
||||
<span style="color:#333;">{{ task.due_date }}</span>
|
||||
{% if task.completed_at and task.status == 'done' %}
|
||||
<div style="font-size:.8rem;color:#6c757d;">Clôturée: {{ task.completed_at }}</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<td style="white-space:nowrap;text-align:right;">
|
||||
{{ priority_badge(task.priority) }}
|
||||
</td>
|
||||
</tr>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro render_tasks_table(tasks, show_entity=True, show_due_date=True, empty_text="Aucune tâche") -%}
|
||||
<div style="overflow:auto;">
|
||||
<table style="width:100%;border-collapse:collapse;">
|
||||
<thead>
|
||||
<tr style="text-align:left;border-bottom:1px solid #e5e5e5;">
|
||||
<th style="padding:.6rem .4rem;">Statut</th>
|
||||
<th style="padding:.6rem .4rem;">Tâche</th>
|
||||
{% if show_entity %}<th style="padding:.6rem .4rem;">Liée à</th>{% endif %}
|
||||
{% if show_due_date %}<th style="padding:.6rem .4rem;">Échéance</th>{% endif %}
|
||||
<th style="padding:.6rem .4rem;text-align:right;">Priorité</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if tasks and tasks|length > 0 %}
|
||||
{% for task in tasks %}
|
||||
{{ render_task_row(task, show_entity=show_entity, show_due_date=show_due_date) }}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="{{ 3 + (1 if show_entity else 0) + (1 if show_due_date else 0) }}" style="padding:.8rem;color:#777;">
|
||||
{{ empty_text }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
127
Templates/tasks/manage.html
Normal file
127
Templates/tasks/manage.html
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
{% extends 'layouts/base.html' %}
|
||||
|
||||
{% block title %}Suite Consultance - Tasks{% endblock %}
|
||||
{% block module_name %}Tasks{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% import "tasks/macros.html" as tsk %}
|
||||
{% import "tasks/manage_macros.html" as tskm %}
|
||||
<div class="container" style="max-width:1100px;">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h1 class="h4 mb-0">Gestion des tâches</h1>
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createTaskModal">
|
||||
<i class="fas fa-plus me-1"></i> Nouvelle tâche
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form class="card p-3 mb-3" method="get">
|
||||
<div class="row g-2">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Statut</label>
|
||||
<select name="status" class="form-select" onchange="this.form.submit()">
|
||||
<option value="" {% if not status %}selected{% endif %}>Tous</option>
|
||||
<option value="todo" {% if status=='todo' %}selected{% endif %}>À faire</option>
|
||||
<option value="done" {% if status=='done' %}selected{% endif %}>Faites</option>
|
||||
<option value="canceled" {% if status=='canceled' %}selected{% endif %}>Annulées</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Type d’entité</label>
|
||||
<select name="entity_type" class="form-select">
|
||||
<option value="" {% if not entity_type %}selected{% endif %}>Toutes</option>
|
||||
<option value="client" {% if entity_type=='client' %}selected{% endif %}>Client</option>
|
||||
<option value="prospect" {% if entity_type=='prospect' %}selected{% endif %}>Prospect</option>
|
||||
<option value="project" {% if entity_type=='project' %}selected{% endif %}>Projet</option>
|
||||
<option value="campaign" {% if entity_type=='campaign' %}selected{% endif %}>Campagne</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">ID entité</label>
|
||||
<input type="text" name="entity_id" class="form-control" value="{{ entity_id or '' }}" placeholder="Filtrer par ID">
|
||||
</div>
|
||||
<div class="col-md-2 d-flex align-items-end">
|
||||
<button class="btn btn-outline-secondary w-100" type="submit">
|
||||
<i class="fas fa-filter me-1"></i> Filtrer
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="card p-3">
|
||||
{{ tskm.render_tasks_table_actions(tasks, show_entity=True, show_due_date=True) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Modal de création global #}
|
||||
{% include 'partials/task_create_modal.html' %}
|
||||
|
||||
{# Modal d'édition de tâche #}
|
||||
<div class="modal fade" id="editTaskModal" tabindex="-1" aria-labelledby="editTaskModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<form method="post" action="{{ url_for('tasks.update_task') }}" class="modal-content">
|
||||
<input type="hidden" name="task_id" id="edit_task_id">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="editTaskModalLabel"><i class="fas fa-pen me-2"></i>Éditer la 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" id="edit_title" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Échéance *</label>
|
||||
<input type="date" name="due_date" id="edit_due_date" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Description</label>
|
||||
<textarea name="description" id="edit_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" id="edit_priority" class="form-select">
|
||||
<option value="basse">Basse</option>
|
||||
<option value="normale">Normale</option>
|
||||
<option value="haute">Haute</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3 col-md-6">
|
||||
<label class="form-label">Type d’entité</label>
|
||||
<select name="entity_type" id="edit_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 l’entité (optionnel)</label>
|
||||
<input type="text" name="entity_id" id="edit_entity_id" class="form-control" placeholder="cli_xxx / pros_xxx / id projet...">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" type="submit"><i class="fas fa-save me-1"></i> Enregistrer</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
document.addEventListener('click', function(e){
|
||||
const btn = e.target.closest('.btn-edit-task');
|
||||
if (!btn) return;
|
||||
document.getElementById('edit_task_id').value = btn.dataset.id || '';
|
||||
document.getElementById('edit_title').value = btn.dataset.title || '';
|
||||
document.getElementById('edit_due_date').value = btn.dataset.due_date || '';
|
||||
document.getElementById('edit_priority').value = btn.dataset.priority || 'normale';
|
||||
document.getElementById('edit_description').value = btn.dataset.description || '';
|
||||
document.getElementById('edit_entity_type').value = btn.dataset.entity_type || '';
|
||||
document.getElementById('edit_entity_id').value = btn.dataset.entity_id || '';
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
133
Templates/tasks/manage_macros.html
Normal file
133
Templates/tasks/manage_macros.html
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
{# Macros dédiés à la page de gestion des tâches (évite les collisions de signatures) #}
|
||||
{% macro _priority_badge(priority) -%}
|
||||
{% set cls = {'haute':'bg-danger','normale':'bg-primary','basse':'bg-success'} %}
|
||||
<span class="badge {{ cls.get(priority, 'bg-primary') }}">{{ priority|capitalize }}</span>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro _status_badge(status) -%}
|
||||
{% set cls = {'todo':'bg-warning text-dark','done':'bg-success','canceled':'bg-danger'} %}
|
||||
{% set labels = {'todo': 'À faire', 'done': 'Fait', 'canceled': 'Annulée'} %}
|
||||
<span class="badge {{ cls.get(status, 'bg-secondary') }}">{{ labels.get(status, status) }}</span>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro _entity_badge(entity_type) -%}
|
||||
{% if entity_type %}
|
||||
{% set labels = {'client': 'Client', 'prospect': 'Prospect', 'project': 'Projet', 'campaign': 'Campagne'} %}
|
||||
<span class="badge bg-secondary-subtle text-dark">{{ labels.get(entity_type, entity_type|capitalize) }}</span>
|
||||
{% endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro render_tasks_table_actions(tasks, show_entity=True, show_due_date=True, empty_text="Aucune tâche") -%}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
{% set cols = 3 + (1 if show_entity else 0) + (1 if show_due_date else 0) + 1 %}
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Statut</th>
|
||||
<th>Tâche</th>
|
||||
{% if show_entity %}<th>Liée à</th>{% endif %}
|
||||
{% if show_due_date %}<th>Échéance</th>{% endif %}
|
||||
<th class="text-end">Priorité</th>
|
||||
<th class="text-end">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if tasks and tasks|length > 0 %}
|
||||
{% for task in tasks %}
|
||||
<tr>
|
||||
<td class="text-nowrap">
|
||||
{{ _status_badge(task.status) }}
|
||||
</td>
|
||||
<td>
|
||||
<div class="fw-semibold">{{ task.title }}</div>
|
||||
{% if task.description %}
|
||||
<div class="small text-muted">{{ task.description }}</div>
|
||||
{% endif %}
|
||||
{% if task.metadata and task.metadata.ref %}
|
||||
<div class="small text-secondary">Ref: {{ task.metadata.ref }}</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if show_entity %}
|
||||
<td class="text-nowrap">
|
||||
{{ _entity_badge(task.entity_type) }}
|
||||
{% if task.entity_id %}
|
||||
<div class="small text-muted">{{ task.entity_id }}</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
{% if show_due_date %}
|
||||
<td class="text-nowrap">
|
||||
<span>{{ task.due_date }}</span>
|
||||
{% if task.completed_at and task.status == 'done' %}
|
||||
<div class="small text-muted">Clôturée: {{ task.completed_at }}</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<td class="text-nowrap text-end">
|
||||
{{ _priority_badge(task.priority) }}
|
||||
</td>
|
||||
<td class="text-nowrap text-end">
|
||||
<div class="d-flex gap-1 justify-content-end">
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-outline-secondary btn-edit-task"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#editTaskModal"
|
||||
data-id="{{ task.id }}"
|
||||
data-title="{{ task.title }}"
|
||||
data-due_date="{{ task.due_date }}"
|
||||
data-priority="{{ task.priority }}"
|
||||
data-description="{{ task.description or '' }}"
|
||||
data-entity_type="{{ task.entity_type or '' }}"
|
||||
data-entity_id="{{ task.entity_id or '' }}"
|
||||
title="Éditer">
|
||||
<i class="fas fa-pen"></i>
|
||||
</button>
|
||||
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-sm btn-outline-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false" title="Changer le statut">
|
||||
<i class="fas fa-exchange-alt"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li>
|
||||
<form method="post" action="{{ url_for('tasks.set_status') }}">
|
||||
<input type="hidden" name="task_id" value="{{ task.id }}">
|
||||
<input type="hidden" name="status" value="todo">
|
||||
<button type="submit" class="dropdown-item {% if task.status=='todo' %}active{% endif %}">À faire</button>
|
||||
</form>
|
||||
</li>
|
||||
<li>
|
||||
<form method="post" action="{{ url_for('tasks.set_status') }}">
|
||||
<input type="hidden" name="task_id" value="{{ task.id }}">
|
||||
<input type="hidden" name="status" value="done">
|
||||
<button type="submit" class="dropdown-item {% if task.status=='done' %}active{% endif %}">Fait</button>
|
||||
</form>
|
||||
</li>
|
||||
<li>
|
||||
<form method="post" action="{{ url_for('tasks.set_status') }}">
|
||||
<input type="hidden" name="task_id" value="{{ task.id }}">
|
||||
<input type="hidden" name="status" value="canceled">
|
||||
<button type="submit" class="dropdown-item {% if task.status=='canceled' %}active{% endif %}">Annulée</button>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<form method="post" action="{{ url_for('tasks.delete_task') }}" onsubmit="return confirm('Supprimer cette tâche ?');">
|
||||
<input type="hidden" name="task_id" value="{{ task.id }}">
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger" title="Supprimer">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="{{ cols }}" class="text-muted"> {{ empty_text }} </td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
16
Templates/tasks/quick_add.html
Normal file
16
Templates/tasks/quick_add.html
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{% extends "layouts/base.html" %}
|
||||
|
||||
{% block title %}Nouvelle tâche liée{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h2>Ajouter une tâche liée</h2>
|
||||
<p class="text-muted">Cette tâche sera liée à l'entité: {{ entity_type }} (ID: {{ entity_id }})</p>
|
||||
|
||||
{% include "partials/task_quick_add_form.html" %}
|
||||
|
||||
<div class="mt-3">
|
||||
<a href="{{ url_for('tasks.tasks_index') }}">← Retour aux tâches</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
10
Templates/tasks/today.html
Normal file
10
Templates/tasks/today.html
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{% import "tasks/macros.html" as tsk %}
|
||||
<div style="max-width:1100px;margin:0 auto;padding:1rem;">
|
||||
<h2 style="margin-top:0;">Tâches du jour</h2>
|
||||
{{ tsk.render_tasks_table(today_tasks, show_entity=True, show_due_date=False, empty_text="Aucune tâche pour aujourd'hui") }}
|
||||
</div>
|
||||
{% import "tasks/macros.html" as tsk %}
|
||||
<div style="max-width:1100px;margin:0 auto;padding:1rem;">
|
||||
<h2 style="margin-top:0;">Tâches du jour</h2>
|
||||
{{ tsk.render_tasks_table(today_tasks, show_entity=True, show_due_date=False, empty_text="Aucune tâche pour aujourd'hui") }}
|
||||
</div>
|
||||
Loading…
Add table
Add a link
Reference in a new issue