refonte(ui): design system moderne et intuitif sur les 5 pages

- en-tête vitré sticky, navigation en pastilles avec état actif,
  logo en tuile dégradée, fil d'Ariane projet
- dashboard: hero + tuiles de synthèse (actifs, fermées/déplacements
  30 j, cycle moyen), barre de progression par projet, états vides
  guidés, raccourci clavier /
- board: sélecteur de milestones + bandeau de progression, colonnes
  à défilement indépendant, placeholders de colonnes vides, hint
  sur le panneau de notes, toast et drawer animés
- live.js: recharge douce conditionnelle (visible, pas de drawer,
  pas de saisie, pas de drag) sinon pastille d'actualisation
- accessibilité: anneau de focus visible, prefers-reduced-motion,
  responsive retravaillé; board.js: drapeau de drag partagé avec
  live.js + raccourci / vers le filtre
This commit is contained in:
Spark
2026-10-11 10:23:50 -04:00
parent 23e63c7f6e
commit dbda3bcf5a
8 changed files with 672 additions and 259 deletions
+51 -9
View File
@@ -9,16 +9,30 @@
<script defer src="/static/vendor/alpine.min.js"></script>
</head>
<body>
<header>
<h1><a href="/">📋 <span class="gradient-text">Kanbante</span></a></h1>
<nav>
<a href="/focus" class="btn-sm">🎯 Focus</a>
<a href="/activity" class="btn-sm">📰 Activité</a>
<span class="badge">{{ active_repos|length }} actifs</span>
<span class="badge secondary">{{ archived_repos|length }} archivés</span>
<header class="app-header">
<div class="brand">
<a href="/" class="logo" aria-label="Kanbante — accueil"><span class="logo-mark">📋</span><span class="gradient-text">Kanbante</span></a>
</div>
<nav class="nav-pills">
<a href="/" class="nav-pill active">🏠 Projets</a>
<a href="/focus" class="nav-pill">🎯 Focus</a>
<a href="/activity" class="nav-pill">📰 Activité</a>
</nav>
</header>
<section class="hero">
<div class="hero-text">
<h2>Bonjour 👋 Voici vos projets Gitea, en un coup d'œil.</h2>
<p>Glissez les cartes, suivez ce qui avance, et laissez Gitea rester la source de vérité.</p>
</div>
<div class="stat-tiles">
<div class="stat-tile"><span class="stat-num">{{ active_repos|length }}</span><span class="stat-label">projets actifs</span></div>
<div class="stat-tile"><span class="stat-num">{{ metrics.closed }}</span><span class="stat-label">issues fermées · 30 j</span></div>
<div class="stat-tile"><span class="stat-num">{{ metrics.moved }}</span><span class="stat-label">déplacements · 30 j</span></div>
<div class="stat-tile"><span class="stat-num">{% if metrics.avg_cycle_days is not none %}{{ metrics.avg_cycle_days }} j{% else %}—{% endif %}</span><span class="stat-label">temps de cycle moyen</span></div>
</div>
</section>
<main x-data="{ tab: 'active', search: '' }">
<!-- Tabs: "actif" = mis à jour dans la fenêtre glissante;
"archivé" = le vrai drapeau Gitea (repo.archived). -->
@@ -37,7 +51,8 @@
<!-- Search: the repo name lives in a data-* attribute (HTML-escaped
by Jinja), never interpolated into a JS string literal. -->
<div class="search-bar">
<input type="text" placeholder="🔍 Filtrer les projets..." x-model="search">
<input type="text" id="project-search" placeholder="🔍 Filtrer les projets..." x-model="search" autocomplete="off">
<kbd class="kbd">/</kbd>
</div>
{% macro project_card(repo, archived=False) %}
@@ -53,8 +68,17 @@
<a href="/board/{{ repo.owner.login }}/{{ repo.name }}">{{ repo.name }}</a>
</h3>
<p class="desc">{{ repo.description or '—' }}</p>
{% if full in stats %}
{% set total = stats[full].values()|sum %}
{% set done_n = stats[full].get('done', 0) %}
{% if total > 0 %}
<div class="progress" title="{{ done_n }}/{{ total }} terminées">
<span style="width: {{ (100 * done_n / total)|round|int }}%"></span>
</div>
{% endif %}
{% endif %}
<div class="card-footer">
<span>{{ repo.open_issues_count or 0 }} issues</span>
<span>{{ repo.open_issues_count or 0 }} issues ouvertes</span>
{% if full in stats %}
<div class="mini-progress">
{% for col in ['backlog','todo','in_progress','review','done'] %}
@@ -73,19 +97,37 @@
<div x-show="tab === 'active'" class="project-grid">
{% for repo in active_repos %}{{ project_card(repo) }}{% endfor %}
{% if not active_repos %}
<div class="empty-state">🌱 Aucun projet actif en ce moment. Les projets bougent dès qu'une issue ou un commit arrive dans Gitea.</div>
{% endif %}
</div>
<div x-show="tab === 'inactive'" class="project-grid">
{% for repo in inactive_repos %}{{ project_card(repo) }}{% endfor %}
{% if not inactive_repos %}
<div class="empty-state">✨ Rien d'endormi : tous vos projets ont eu de l'activité récemment.</div>
{% endif %}
</div>
<div x-show="tab === 'archived'" class="project-grid">
{% for repo in archived_repos %}{{ project_card(repo, archived=True) }}{% endfor %}
{% if not archived_repos %}
<div class="empty-state">🗄️ Aucun projet archivé. L'archivage se fait dans Gitea (Réglages du dépôt).</div>
{% endif %}
</div>
</main>
<footer>
Kanbante v{{ version }} · {{ active_repos|length + inactive_repos|length + archived_repos|length }} projets sur Gitea · actifs = mis à jour depuis {{ active_window_days }} jours
</footer>
<script src="/static/js/live.js" defer></script>
<script>
document.addEventListener('keydown', function (e) {
if (e.key === '/' && !/INPUT|TEXTAREA/.test(document.activeElement?.tagName || '')) {
e.preventDefault();
document.getElementById('project-search')?.focus();
}
});
</script>
</body>
</html>