format_standup_text + _standup_data (journal de la veille, focus du jour, vieillissantes QA/Révision); page /standup copiable, /api/standup; inclusion optionnelle au briefing ntfy (KANBANTE_BRIEFING_INCLUDE_STANDUP).
76 lines
3.7 KiB
HTML
76 lines
3.7 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>Récit de sprint — Kanbante</title>
|
|
<link rel="stylesheet" href="/static/css/style.css">
|
|
<script src="/static/vendor/marked.min.js"></script>
|
|
<script src="/static/vendor/purify.min.js"></script>
|
|
</head>
|
|
<body>
|
|
<header class="app-header">
|
|
<a href="/" class="logo">📋 Kanbante</a>
|
|
<nav class="nav-pills">
|
|
<a href="/board/{{ full_name }}" class="nav-pill">📋 Board</a>
|
|
<a href="/today" class="nav-pill">🌅 Aujourd'hui</a>
|
|
<a href="/forecast" class="nav-pill">🔮 Prévisions</a>
|
|
<a href="/rules" class="nav-pill">⚙️ Règles</a>
|
|
<button type="button" class="nav-pill palette-trigger" title="Palette de commandes (Ctrl+K)">⌨️ Ctrl K</button>
|
|
</nav>
|
|
</header>
|
|
|
|
<main class="focus-main">
|
|
{% if markdown %}
|
|
<section class="briefing-card">
|
|
<h1>📖 {{ milestone_title }}</h1>
|
|
<p>
|
|
<button type="button" class="btn-save btn-inline" id="sprint-copy">📋 Copier le markdown</button>
|
|
<button type="button" class="btn-save btn-inline" id="sprint-download">⬇️ Télécharger (.md)</button>
|
|
<a class="btn-save btn-inline" style="text-decoration:none" href="/board/{{ full_name }}?milestone={{ milestone_param|urlencode }}">🎯 Voir le board du milestone</a>
|
|
</p>
|
|
<p class="notes-hint">Le texte est archivable tel quel dans ObsiGate — c'est le markdown ci-dessous, rendu pour lecture.</p>
|
|
</section>
|
|
<section class="briefing-card sprint-render" id="sprint-render"></section>
|
|
<script>
|
|
const SPRINT_MD = {{ markdown|tojson }};
|
|
document.getElementById('sprint-render').innerHTML =
|
|
DOMPurify.sanitize(marked.parse(SPRINT_MD));
|
|
document.getElementById('sprint-copy').addEventListener('click', async () => {
|
|
try {
|
|
await navigator.clipboard.writeText(SPRINT_MD);
|
|
document.getElementById('sprint-copy').textContent = '✅ Copié !';
|
|
} catch (e) { alert('Copie impossible : ' + e); }
|
|
});
|
|
document.getElementById('sprint-download').addEventListener('click', () => {
|
|
const blob = new Blob([SPRINT_MD], { type: 'text/markdown' });
|
|
const a = document.createElement('a');
|
|
a.href = URL.createObjectURL(blob);
|
|
a.download = 'recit-{{ full_name|replace('/', '-') }}-{{ milestone_param|urlencode }}.md';
|
|
a.click();
|
|
URL.revokeObjectURL(a.href);
|
|
});
|
|
</script>
|
|
{% else %}
|
|
<section class="briefing-card">
|
|
<h1>📖 Récit de sprint — {{ full_name }}</h1>
|
|
<p>Choisis un milestone pour générer son récit :</p>
|
|
<ul>
|
|
{% for m in milestones %}
|
|
<li><a href="/sprint/{{ full_name }}?milestone={{ m.title|urlencode }}">🎯 {{ m.title }}</a> — {{ m.closed_issues or 0 }} fermée(s), {{ m.open_issues or 0 }} ouverte(s)</li>
|
|
{% else %}
|
|
<li>Aucun milestone dans ce projet.</li>
|
|
{% endfor %}
|
|
</ul>
|
|
</section>
|
|
{% endif %}
|
|
</main>
|
|
<footer>
|
|
<span>Kanbante v{{ version }} · le récit est écrit par le journal, pas par la mémoire des gens</span>
|
|
</footer>
|
|
{% include "_palette.html" %}
|
|
<script src="/static/js/palette.js" defer></script>
|
|
</body>
|
|
</html>
|