CI / test (push) Successful in 2m22s
- Clients : create_milestone / update_milestone (cache invalidé) ; GitHub hérite des mêmes endpoints. - API Kanbante : création, modification (titre/échéance, échéance vidée = retirée), fermeture ; CSRF, validation de date avant toute écriture, erreurs forge en 502. - Page Projet : formulaire 'Nouveau jalon' + édition/ fermeture par jalon (project.js versionné). - Échéance à midi UTC : à minuit, le fuseau de l'instance Gitea faisait reculer la date affichée d'un jour (réel). - 291 tests verts (5 nouveaux, palier 24) ; cycle réel complet sur Projets/kanbante (jalons smoke #37/#38 créés, dates vérifiées, fermés).
161 lines
8.9 KiB
HTML
161 lines
8.9 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">
|
||
<title>{{ full_name }} — Projet · Kanbante</title>
|
||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📋</text></svg>">
|
||
<meta name="csrf-token" content="{{ csrf_token }}">
|
||
<meta name="repo-full-name" content="{{ full_name }}">
|
||
<link rel="stylesheet" href="/static/css/style.css?v={{ version }}">
|
||
</head>
|
||
<body>
|
||
{% include "_header.html" %}
|
||
|
||
<main class="project-main">
|
||
<div class="proj-head">
|
||
<div>
|
||
<h1>📦 {{ repo_info.name }}</h1>
|
||
<p class="proj-sub">{{ owner }} · {{ repo_info.description or 'Aucune description dans Gitea.' }}</p>
|
||
</div>
|
||
{% if health_dot %}
|
||
<a href="/health/{{ full_name }}" class="health-chip" title="{{ health_dot.tooltip }}">
|
||
<span class="health-dot {{ health_dot.level }}"></span> Santé du flux
|
||
</a>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div class="proj-actions">
|
||
<a href="/board/{{ full_name }}" class="nav-pill proj-primary">📋 Ouvrir le board</a>
|
||
<a href="/capture" class="nav-pill">⚡ Capture</a>
|
||
<a href="/triage/{{ full_name }}" class="nav-pill">📥 Triage</a>
|
||
<a href="/health/{{ full_name }}" class="nav-pill">🩺 Santé</a>
|
||
<a href="/forecast" class="nav-pill">🔮 Prévisions</a>
|
||
<a href="/wall?repo={{ full_name }}" class="nav-pill">🖥️ Wallboard</a>
|
||
{% if repo_info.html_url %}
|
||
<a href="{{ repo_info.html_url }}" target="_blank" rel="noopener" class="nav-pill">🔗 Gitea ↗</a>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div class="proj-grid">
|
||
<section class="proj-panel">
|
||
<h2>🎯 Jalons ouverts</h2>
|
||
{% if milestones %}
|
||
{% for ms in milestones %}
|
||
<div class="proj-ms">
|
||
<div class="ms-line">
|
||
<a href="/board/{{ full_name }}?milestone={{ ms.title|urlencode }}"><strong>{{ ms.title }}</strong></a>
|
||
{% if ms.due_label %}<span class="ms-due{% if ms.late %} late{% endif %}">{{ '⚠️ ' if ms.late }}échéance {{ ms.due_label }}</span>{% endif %}
|
||
</div>
|
||
{% if ms.total %}
|
||
<div class="progress ms-progress"><span style="width: {{ ms.pct }}%"></span></div>
|
||
<div class="ms-meta">{{ ms.closed }}/{{ ms.total }} terminées{% if ms.eta_label %} · 🔮 {{ ms.eta_label }}{% endif %}</div>
|
||
{% else %}
|
||
<div class="ms-meta">Aucune issue dans ce jalon pour le moment.</div>
|
||
{% endif %}
|
||
{% if ms.id %}
|
||
<details class="ms-edit">
|
||
<summary>✏️ Modifier / fermer</summary>
|
||
<form class="ms-form" data-ms-form data-action="/api/repos/{{ full_name }}/milestones/{{ ms.id }}/update">
|
||
<label>Titre <input type="text" name="title" value="{{ ms.title }}" maxlength="255" required></label>
|
||
<label>Échéance <input type="date" name="due_on" value="{{ ms.due_iso }}">
|
||
<span class="ms-hint">vide = retirer l'échéance</span></label>
|
||
<div class="ms-form-actions">
|
||
<button type="submit" class="btn-save btn-inline">💾 Enregistrer</button>
|
||
</div>
|
||
</form>
|
||
<form class="ms-form" data-ms-form data-action="/api/repos/{{ full_name }}/milestones/{{ ms.id }}/close"
|
||
data-confirm="Fermer le jalon « {{ ms.title }} » ? Ses issues restent ouvertes, sans jalon actif.">
|
||
<button type="submit" class="btn-mini btn-danger">Fermer le jalon</button>
|
||
</form>
|
||
</details>
|
||
{% endif %}
|
||
</div>
|
||
{% endfor %}
|
||
{% else %}
|
||
<p class="proj-empty">Aucun jalon ouvert pour le moment.</p>
|
||
{% endif %}
|
||
<details class="ms-edit ms-new">
|
||
<summary>➕ Nouveau jalon</summary>
|
||
<form class="ms-form" data-ms-form data-action="/api/repos/{{ full_name }}/milestones">
|
||
<label>Titre <input type="text" name="title" maxlength="255" required placeholder="Ex. : Version 2.0"></label>
|
||
<label>Échéance <input type="date" name="due_on"></label>
|
||
<label>Description <textarea name="description" rows="2" maxlength="2000" placeholder="Optionnelle"></textarea></label>
|
||
<div class="ms-form-actions">
|
||
<button type="submit" class="btn-save btn-inline">🎯 Créer le jalon</button>
|
||
</div>
|
||
</form>
|
||
</details>
|
||
</section>
|
||
|
||
<section class="proj-panel">
|
||
<h2>🩺 Santé du flux</h2>
|
||
{% if health %}
|
||
<ul class="proj-list">
|
||
<li>📂 <strong>{{ health.open_count }}</strong> cartes ouvertes</li>
|
||
{% if health.slowest %}
|
||
<li>🐢 Goulot : <strong>{{ COLUMN_LABELS[health.slowest.column] }}</strong> — séjour médian {{ health.slowest.median_days }} j</li>
|
||
{% else %}
|
||
<li>🐢 Pas encore de goulot mesurable (historique trop court).</li>
|
||
{% endif %}
|
||
<li>🧟 Zombies (≥ {{ health.zombie_days }} j sans activité) : <strong>{{ health.zombies|length }}</strong></li>
|
||
</ul>
|
||
{% if health.zombies %}
|
||
<ul class="proj-list proj-zombies">
|
||
{% for z in health.zombies[:5] %}
|
||
<li><a href="{{ z.html_url }}" target="_blank" rel="noopener">#{{ z.number }}</a> {{ z.title }} <span class="proj-dim">· {{ z.column_label }} · {{ z.days_inactive }} j</span></li>
|
||
{% endfor %}
|
||
</ul>
|
||
{% endif %}
|
||
<p><a href="/health/{{ full_name }}">Détail de la santé →</a></p>
|
||
{% else %}
|
||
<p class="proj-empty">Santé indisponible pour le moment.</p>
|
||
{% endif %}
|
||
</section>
|
||
|
||
<section class="proj-panel">
|
||
<h2>🔮 Prévisions</h2>
|
||
{% if throughput %}
|
||
<ul class="proj-list">
|
||
<li>Débit : <strong>{{ throughput.avg }}</strong> fermetures / semaine en moyenne (min {{ throughput.min }} · max {{ throughput.max }}, 4 dernières semaines complètes)</li>
|
||
<li>Issues ouvertes : <strong>{{ repo_info.open_issues_count or 0 }}</strong></li>
|
||
</ul>
|
||
{% else %}
|
||
<p class="proj-empty">Pas assez d'historique de fermetures pour prévoir — le journal se remplit au fil des cartes terminées.</p>
|
||
{% endif %}
|
||
<p><a href="/forecast">Toutes les prévisions →</a></p>
|
||
</section>
|
||
|
||
<section class="proj-panel">
|
||
<h2>🕘 Activité récente</h2>
|
||
{% if events %}
|
||
<ul class="proj-list">
|
||
{% for e in events %}
|
||
<li>{{ EVENT_ICONS.get(e.event_type, '•') }} <a href="/board/{{ full_name }}">#{{ e.issue_number }}</a> — {{ e.event_type }}{% if e.to_column %} → {{ COLUMN_LABELS.get(e.to_column, e.to_column) }}{% endif %} <span class="proj-dim">· {{ e.created_at[:16].replace('T', ' ') }}</span></li>
|
||
{% endfor %}
|
||
</ul>
|
||
{% else %}
|
||
<p class="proj-empty">Aucun événement du journal pour ce projet pour le moment.</p>
|
||
{% endif %}
|
||
<p><a href="/activity">Journal complet →</a></p>
|
||
</section>
|
||
|
||
<section class="proj-panel proj-notes">
|
||
<h2>📝 Notes du projet</h2>
|
||
{% if notes %}
|
||
<div class="proj-notes-body">{{ notes }}</div>
|
||
{% else %}
|
||
<p class="proj-empty">Aucune note. Les notes s'écrivent depuis le panneau « 🗒️ Notes » du board et vivent avec le projet.</p>
|
||
{% endif %}
|
||
<p><a href="/board/{{ full_name }}">Ouvrir le board pour éditer →</a></p>
|
||
</section>
|
||
</div>
|
||
</main>
|
||
|
||
<footer>Kanbante v{{ version }} · Page projet — jalons, santé, prévisions, activité et notes en un coup d'œil.</footer>
|
||
{% include "_palette.html" %}
|
||
<script src="/static/js/project.js?v={{ version }}" defer></script>
|
||
<script src="/static/js/palette.js" defer></script>
|
||
</body>
|
||
</html>
|