- vendor: HTMX/Alpine/SortableJS servis depuis /static/vendor (M3) - board.js: jeton CSRF, ordres source/destination envoyés, carte replacée + toast rouge en cas d'échec (M8), filtres texte/étiquette, formulaire de création d'issue - dashboard: 3 onglets Actifs/Inactifs/Archivés, recherche via data-search échappé au lieu d'une interpolation JS (M9) - board: cartes liées vers l'issue Gitea, méta CSRF, notes protégées - nouvelles pages focus.html et error.html - CSS: fond des badges en color-mix, l'ancien var(--accent)20 était invalide (M1); styles toolbar/focus/erreur/toast erreur
120 lines
5.1 KiB
HTML
120 lines
5.1 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="fr" data-theme="dark">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<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/js/board.js" defer></script>
|
||
</head>
|
||
<body>
|
||
<header>
|
||
<h1>
|
||
<a href="/">📋 Kanbante</a>
|
||
<span class="sep">/</span>
|
||
<span>{{ owner }}/</span><strong>{{ repo }}</strong>
|
||
</h1>
|
||
<nav>
|
||
<a href="/focus" class="btn-sm">🎯 Focus</a>
|
||
{% if repo_info.html_url %}
|
||
<a href="{{ repo_info.html_url }}" target="_blank" class="btn-sm">🔗 Gitea</a>
|
||
{% endif %}
|
||
</nav>
|
||
</header>
|
||
|
||
<!-- Toolbar: text + label filters, and issue creation (Gitea-backed) -->
|
||
<div class="board-toolbar">
|
||
<input type="text" id="card-filter" placeholder="🔍 Filtrer les cartes...">
|
||
<select id="label-filter">
|
||
<option value="">Toutes les étiquettes</option>
|
||
{% for label in labels %}
|
||
<option value="{{ label.name }}">{{ label.name }}</option>
|
||
{% endfor %}
|
||
</select>
|
||
<form id="create-issue-form">
|
||
<input type="text" id="new-issue-title" maxlength="255"
|
||
placeholder="➕ Nouvelle issue (titre)..." required>
|
||
<button type="submit" class="btn-save btn-inline">Créer</button>
|
||
</form>
|
||
</div>
|
||
|
||
<main class="board-container">
|
||
<div class="kanban-board">
|
||
{% for col in ['backlog','todo','in_progress','review','done'] %}
|
||
<div class="kanban-column"
|
||
data-column="{{ col }}">
|
||
<div class="column-header" style="border-top: 3px solid {{ column_colors[col] }}">
|
||
<span class="column-title">{{ column_labels[col] }}</span>
|
||
<span class="column-count">{{ columns[col]|length }}</span>
|
||
</div>
|
||
<div class="column-cards" data-column="{{ col }}">
|
||
{% for issue in columns[col] %}
|
||
<div class="kanban-card"
|
||
data-issue="{{ issue.number }}"
|
||
data-repo="{{ full_name }}"
|
||
data-title="{{ issue.title|lower }}"
|
||
data-labels="{{ issue.labels|map(attribute='name')|join(',')|lower }}">
|
||
<div class="card-id">
|
||
#{{ issue.number }}
|
||
{% if issue.html_url %}
|
||
<a href="{{ issue.html_url }}" target="_blank" title="Ouvrir dans Gitea">↗</a>
|
||
{% endif %}
|
||
</div>
|
||
<div class="card-title">{{ issue.title }}</div>
|
||
{% if issue.labels %}
|
||
<div class="card-labels">
|
||
{% for label in issue.labels %}
|
||
<span class="label-tag" style="background: #{{ label.color }}30; color: #{{ label.color }}">
|
||
{{ label.name }}
|
||
</span>
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
<div class="card-meta">
|
||
{% if issue.assignee %}
|
||
<img src="{{ issue.assignee.avatar_url }}" class="avatar" alt="{{ issue.assignee.login }}">
|
||
{% endif %}
|
||
<span class="card-date">{{ issue.created_at[:10] }}</span>
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
|
||
<!-- Notes panel -->
|
||
<aside class="notes-panel">
|
||
<h3>📝 Notes du projet</h3>
|
||
<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>
|
||
</aside>
|
||
</main>
|
||
|
||
<footer>
|
||
<span>{{ full_name }}</span>
|
||
{% if repo_info.updated_at %}
|
||
<span>Mis à jour {{ repo_info.updated_at[:10] }}</span>
|
||
{% endif %}
|
||
</footer>
|
||
<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>
|