feat(board): étiquettes gérables, notes repliable/redimensionnable/auto-save

- modale Étiquettes: liste, création (nom + couleur + palette),
  suppression via DELETE (CSRF par en-tête); panneau détail:
  bascules d'étiquettes par issue avec mise à jour immédiate des
  chips de la carte; _issue_payload expose les ids d'étiquettes
- API étiquettes dans main (routes repos + toggle issue)
- notes: bouton replier/déplier et largeur par glisser, persistés
  par projet (localStorage), sauvegarde automatique débouncée
  avec statut, formulaire repris par board.js (HTMX retiré du board)
- nouvelles pages search/my/briefing; navigation enrichie partout
  (Mes issues, Briefing, Recherche); CSS associé
This commit is contained in:
Spark
2026-10-11 11:00:53 -04:00
parent a788db7b1f
commit 6887d631ea
10 changed files with 621 additions and 22 deletions
+49 -22
View File
@@ -6,7 +6,6 @@
<meta name="csrf-token" content="{{ csrf_token }}">
<title>{{ repo }} — Kanbante</title>
<link rel="stylesheet" href="/static/css/style.css">
<script src="/static/vendor/htmx.min.js"></script>
<script src="/static/vendor/sortable.min.js"></script>
<script src="/static/vendor/marked.min.js"></script>
<script src="/static/vendor/purify.min.js"></script>
@@ -21,7 +20,9 @@
<nav class="nav-pills">
<a href="/" class="nav-pill">🏠 Projets</a>
<a href="/focus" class="nav-pill">🎯 Focus</a>
<a href="/my" class="nav-pill">👤 Mes issues</a>
<a href="/activity" class="nav-pill">📰 Activité</a>
<a href="/search" class="nav-pill">🔍 Recherche</a>
{% if repo_info.html_url %}
<a href="{{ repo_info.html_url }}" target="_blank" class="nav-pill">🔗 Gitea ↗</a>
{% endif %}
@@ -61,6 +62,7 @@
<option value="{{ label.name }}">{{ label.name }}</option>
{% endfor %}
</select>
<button type="button" id="labels-btn" class="btn-sm">🏷️ Gérer les étiquettes</button>
<form id="create-issue-form">
<input type="text" id="new-issue-title" maxlength="255"
placeholder="➕ Nouvelle issue (titre)..." required>
@@ -123,21 +125,51 @@
{% endfor %}
</div>
<!-- Notes panel -->
<aside class="notes-panel">
<h3>📝 Notes du projet</h3>
<p class="notes-hint">Contexte libre, propre à Kanbante — il ne part pas dans Gitea.</p>
<form hx-post="/api/notes" hx-swap="none"
hx-on::after-request="if(event.detail.successful) showNotesSaved()">
<input type="hidden" name="repo_full_name" value="{{ full_name }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<textarea name="note" rows="10"
placeholder="Notes, idées, liens...">{{ notes }}</textarea>
<button type="submit" class="btn-save">💾 Sauvegarder</button>
</form>
<!-- Notes panel: collapsible + resizable + autosave -->
<aside class="notes-panel" id="notes-panel">
<div class="notes-resize" id="notes-resize" title="Glisser pour ajuster la largeur"></div>
<div class="notes-head">
<h3>📝 Notes du projet</h3>
<button type="button" id="notes-toggle" class="btn-sm notes-toggle"
title="Replier / déplier le panneau">⤢</button>
</div>
<div class="notes-body-wrap">
<p class="notes-hint">Contexte libre, propre à Kanbante — il ne part pas dans Gitea. Enregistrement automatique.</p>
<form id="notes-form">
<input type="hidden" name="repo_full_name" value="{{ full_name }}">
<textarea name="note" id="notes-text" rows="10"
placeholder="Notes, idées, liens...">{{ notes }}</textarea>
<div class="notes-foot">
<span id="notes-status" class="notes-status"></span>
<button type="submit" class="btn-save">💾 Sauvegarder</button>
</div>
</form>
</div>
</aside>
</main>
<!-- Label manager modal -->
<div id="label-modal" class="modal" hidden>
<div class="modal-box">
<div class="detail-head">
<h2>🏷️ Étiquettes de {{ full_name }}</h2>
<button id="label-modal-close" class="btn-sm" type="button">✕</button>
</div>
<p class="notes-hint">Ces étiquettes vivent dans Gitea : les créer ou les supprimer ici les modifie pour tout le dépôt.</p>
<div id="label-list" class="label-list"></div>
<form id="label-create-form" class="label-create-form">
<input type="text" id="new-label-name" maxlength="50" placeholder="Nom de la nouvelle étiquette" required>
<input type="color" id="new-label-color" value="#7c5cfc" title="Couleur">
<button type="submit" class="btn-save btn-inline">Créer</button>
</form>
<div class="label-swatches">
{% for c in ['e11d48','f59e0b','10b981','0ea5e9','7c5cfc','ec4899','64748b','84cc16'] %}
<button type="button" class="swatch" data-color="#{{ c }}" style="background: #{{ c }}" title="#{{ c }}"></button>
{% endfor %}
</div>
</div>
</div>
<!-- Card detail drawer: issue body, comments, comment form -->
<div id="detail-overlay" class="detail-overlay" hidden></div>
<aside id="card-detail" class="card-detail" hidden>
@@ -149,6 +181,10 @@
<h2 id="detail-title"></h2>
<div id="detail-meta" class="detail-meta"></div>
<div id="detail-labels" class="card-labels"></div>
<div class="detail-labels-manage">
<span class="notes-hint">Étiquettes de l'issue — cliquez pour ajouter / retirer :</span>
<div id="detail-label-toggles" class="label-toggles"></div>
</div>
<div id="detail-body" class="detail-body"></div>
<h3 class="detail-comments-title">💬 Commentaires</h3>
<div id="detail-comments"></div>
@@ -167,14 +203,5 @@
{% endif %}
</footer>
<script src="/static/js/live.js" defer></script>
<script>
function showNotesSaved() {
const toast = document.createElement('div');
toast.className = 'toast';
toast.textContent = '✅ Notes sauvegardées';
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 2000);
}
</script>
</body>
</html>