170 lines
11 KiB
HTML
170 lines
11 KiB
HTML
<script>
|
|
/* Thème (#52) : appliqué avant le rendu de la barre, pour
|
|
éviter le flash sombre quand l'utilisateur a choisi le
|
|
clair. Défaut = sombre (les gabarits portent
|
|
data-theme="dark") ; seul un choix mémorisé bascule. */
|
|
try {
|
|
var _t = localStorage.getItem('kanbante-theme');
|
|
if (_t === 'light' || _t === 'dark') {
|
|
document.documentElement.setAttribute('data-theme', _t);
|
|
}
|
|
} catch (e) { /* mode privé : le thème ne survit qu'à la page */ }
|
|
</script>
|
|
{% include "_draco_bar.html" %}
|
|
{# ── Cadre applicatif partagé (issue #16, refonte 0.16.0) ─────────
|
|
Barre latérale unique pour TOUTES les pages applicatives :
|
|
desktop = barre fixe repliable en rail d'icônes (état mémorisé),
|
|
mobile = tiroir hors-canvas ouvert par le burger de la topbar.
|
|
NE PAS dupliquer ce markup dans les pages.
|
|
Page active : `active_nav` (déduite du gabarit dans render()).
|
|
Fil d'Ariane : `owner`+`repo` (board) ou `full_name` (projet).
|
|
Le wallboard (TV) n'inclut pas ce partiel, volontairement. #}
|
|
{% set nav_groups = [
|
|
("principal", "Principal", [
|
|
("home", "/", "🏠", "Projets"),
|
|
("focus", "/focus", "🎯", "Focus"),
|
|
("today", "/today", "🌅", "Aujourd'hui"),
|
|
("my", "/my", "👤", "Mes issues"),
|
|
]),
|
|
("rituels", "Rituels", [
|
|
("briefing", "/briefing", "📊", "Briefing"),
|
|
("standup", "/standup", "🗣️", "Standup"),
|
|
("digest", "/digest", "🧠", "Digest"),
|
|
]),
|
|
("pilotage", "Pilotage", [
|
|
("inflight", "/inflight", "🛩️", "En vol"),
|
|
("forecast", "/forecast", "🔮", "Prévisions"),
|
|
("health", "/health", "🩺", "Santé"),
|
|
("rules", "/rules", "⚙️", "Règles"),
|
|
("activity", "/activity", "📰", "Activité"),
|
|
]),
|
|
("outils", "Outils", [
|
|
("search", "/search", "", "Recherche"),
|
|
("capture", "/capture", "⚡", "Capture"),
|
|
("archives", "/archives", "🗄️", "Archives"),
|
|
("wall", "/wall", "🖥️", "Wallboard"),
|
|
]),
|
|
] %}
|
|
{% if is_admin %}
|
|
{% set nav_groups = nav_groups + [
|
|
("admin", "Administration", [
|
|
("admin", "/admin", "🛠️", "Vue d'ensemble"),
|
|
("admin_connections", "/admin/connections", "🔌", "Connexions"),
|
|
("admin_workspaces", "/admin/workspaces", "🧭", "Espaces"),
|
|
("admin_columns", "/admin/columns", "🧱", "Colonnes"),
|
|
("admin_settings", "/admin/settings", "🎛️", "Paramètres"),
|
|
]),
|
|
] %}
|
|
{% endif %}
|
|
{% set active_group = namespace(key="principal") %}
|
|
{% for gkey, group, links in nav_groups %}
|
|
{% for key, href, icon, label in links %}
|
|
{% if active_nav == key %}{% set active_group.key = gkey %}{% endif %}
|
|
{% endfor %}
|
|
{% endfor %}
|
|
<script>
|
|
document.body.classList.add('with-sidebar');
|
|
try {
|
|
if (localStorage.getItem('kanbante-sidebar') === 'collapsed') {
|
|
document.body.classList.add('sidebar-collapsed');
|
|
}
|
|
/* Accordéon exclusif (0.22.0) : une seule section ouverte.
|
|
La section de la page courante prime, sinon celle
|
|
mémorisée, sinon « principal ». */
|
|
localStorage.removeItem('kanbante-nav-closed');
|
|
var openGroup = {{ active_group.key | tojson }};
|
|
var stored = localStorage.getItem('kanbante-nav-open');
|
|
var groups = ['principal', 'rituels', 'pilotage', 'outils', 'admin'];
|
|
if ({{ (active_nav != "") | tojson }}) {
|
|
localStorage.setItem('kanbante-nav-open', openGroup);
|
|
} else if (stored === '') {
|
|
openGroup = '';
|
|
} else if (stored !== null && groups.indexOf(stored) >= 0) {
|
|
openGroup = stored;
|
|
}
|
|
groups.forEach(function (k) {
|
|
if (k !== openGroup) document.body.classList.add('nav-closed-' + k);
|
|
});
|
|
} catch (e) { /* stockage indisponible : état par défaut */ }
|
|
</script>
|
|
<aside class="sidebar" id="sidebar" aria-label="Navigation principale">
|
|
<div class="side-head">
|
|
<a href="/" class="side-logo" aria-label="Kanbante — accueil">
|
|
<span class="logo-mark"><svg viewBox="0 0 24 24" fill="#fff" aria-hidden="true"><rect x="3" y="4" width="5" height="16" rx="1"/><rect x="9.5" y="4" width="5" height="10" rx="1"/><rect x="16" y="4" width="5" height="13" rx="1"/></svg></span>
|
|
<span class="side-label gradient-text">Kanbante</span>
|
|
</a>
|
|
<button type="button" class="side-search palette-trigger" title="Recherche & commandes (Ctrl+K)" aria-label="Recherche et commandes (Ctrl+K)"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="M20 20l-4-4"/></svg></button>
|
|
<button type="button" class="side-collapse" id="side-collapse" title="Replier ou déplier le menu" aria-label="Replier ou déplier le menu"><span class="side-collapse-ico">«</span></button>
|
|
</div>
|
|
{% if current_workspace and user_workspaces and user_workspaces|length > 1 %}
|
|
<form method="post" action="/workspace/switch" class="ws-switch">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<span class="ws-dot" style="background: {{ current_workspace.color }}" aria-hidden="true"></span>
|
|
<select name="workspace_id" class="ws-select" onchange="this.form.submit()" title="Changer d'espace de travail" aria-label="Espace de travail">
|
|
{% for ws in user_workspaces %}
|
|
<option value="{{ ws.id }}"{% if ws.id == current_workspace.id %} selected{% endif %}>{{ ws.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</form>
|
|
{% endif %}
|
|
{# Icônes de navigation (#54) : SVG fins en trait, style rail
|
|
Spark — monochromes (currentColor), pas d'emoji. #}
|
|
{% macro nav_icon(key) %}<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">{% if key == 'home' %}<path d="M3 11l9-8 9 8"/><path d="M5 10v11h14V10"/><path d="M10 21v-6h4v6"/>{% elif key == 'focus' %}<circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="5"/><circle cx="12" cy="12" r="1" fill="currentColor"/>{% elif key == 'today' %}<path d="M7 18a5 5 0 0 1 10 0"/><path d="M3 18h18"/><path d="M12 9V6"/><path d="M5.5 11l1.5 1.5"/><path d="M18.5 11l-1.5 1.5"/>{% elif key == 'my' %}<circle cx="12" cy="8" r="3.5"/><path d="M4 20c0-3.5 3.5-5.5 8-5.5s8 2 8 5.5"/>{% elif key == 'briefing' %}<path d="M4 20V11"/><path d="M10 20V4"/><path d="M16 20v-8"/><path d="M3 20h18"/>{% elif key == 'standup' %}<path d="M4 5h16v11h-7l-4 4v-4H4z"/>{% elif key == 'digest' %}<path d="M6 3h12v18H6z"/><path d="M9 8h6"/><path d="M9 12h6"/><path d="M9 16h4"/>{% elif key == 'inflight' %}<path d="M3 12l19-9-7 18-4-6z"/><path d="M11 15L22 3"/>{% elif key == 'forecast' %}<path d="M3 18l5-6 4 3 9-9"/><path d="M16 6h5v5"/>{% elif key == 'health' %}<path d="M12 20C7 16 3 13 3 9a4.5 4.5 0 0 1 8-2.5A4.5 4.5 0 0 1 21 9c0 4-4 7-9 11z"/>{% elif key == 'rules' %}<path d="M6 3h12v18H6z"/><path d="M9 12l2 2 4-4"/>{% elif key == 'activity' %}<path d="M4 4h16v16H4z"/><path d="M7 8h10"/><path d="M7 12h10"/><path d="M7 16h6"/>{% elif key == 'search' %}<circle cx="11" cy="11" r="7"/><path d="M20 20l-4-4"/>{% elif key == 'capture' %}<path d="M13 2L4 14h6l-1 8 10-13h-6z"/>{% elif key == 'archives' %}<path d="M3 8h18"/><path d="M5 8v12h14V8"/><path d="M9 8v5h6V8"/>{% elif key == 'wall' %}<path d="M3 5h18v11H3z"/><path d="M9 21h6"/><path d="M12 16v5"/>{% elif key == 'admin' %}<path d="M4 4h7v7H4z"/><path d="M13 4h7v7h-7z"/><path d="M4 13h7v7H4z"/><path d="M13 13h7v7h-7z"/>{% elif key == 'admin_connections' %}<path d="M9 3v5"/><path d="M15 3v5"/><path d="M7 8h10v5a5 5 0 0 1-10 0z"/><path d="M12 18v3"/>{% elif key == 'admin_workspaces' %}<circle cx="12" cy="12" r="9"/><path d="M15 9l-2 4-4 2 2-4z"/>{% elif key == 'admin_columns' %}<path d="M4 4h4v16H4z"/><path d="M10 4h4v16h-4z"/><path d="M16 4h4v16h-4z"/>{% elif key == 'admin_settings' %}<circle cx="12" cy="12" r="3"/><path d="M12 2v3"/><path d="M12 19v3"/><path d="M2 12h3"/><path d="M19 12h3"/><path d="M4.5 4.5l2 2"/><path d="M17.5 17.5l2 2"/><path d="M19.5 4.5l-2 2"/><path d="M6.5 17.5l-2 2"/>{% else %}<circle cx="12" cy="12" r="9"/>{% endif %}</svg>{% endmacro %}
|
|
<nav class="side-nav">
|
|
{% for gkey, group, links in nav_groups %}
|
|
<div class="side-section" data-group="{{ gkey }}">
|
|
<button type="button" class="side-group-btn" data-group-btn="{{ gkey }}" aria-expanded="true">
|
|
<span>{{ group }}</span>
|
|
<span class="side-chevron" aria-hidden="true">▾</span>
|
|
</button>
|
|
<div class="side-acc">
|
|
<div class="side-acc-inner">
|
|
{% for key, href, icon, label in links %}
|
|
<a href="{{ href }}" class="side-link{% if active_nav == key %} active{% endif %}" title="{{ label }}">
|
|
<span class="side-ico">{{ nav_icon(key) }}</span>
|
|
<span class="side-label">{{ label }}</span>
|
|
</a>
|
|
{% endfor %}
|
|
{% if gkey == 'admin' %}
|
|
<button type="button" class="side-link theme-toggle-link" id="theme-toggle" title="Basculer entre thème sombre et clair" aria-label="Basculer entre thème sombre et clair">
|
|
<span class="side-ico" data-theme-ico>☀️</span>
|
|
<span class="side-label" data-theme-label>Mode clair</span>
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</nav>
|
|
<div class="side-foot">
|
|
{% if data_freshness %}
|
|
<span class="side-fresh side-label" title="Dernière actualisation des données Gitea (instantanés d'arrière-plan)">🕐 {{ data_freshness }}</span>
|
|
{% endif %}
|
|
{% if current_user %}
|
|
<a href="/profile" class="side-logout" title="Mon profil">👤<span class="side-label"> {{ current_user.display_name or current_user.username }}</span></a>
|
|
{% endif %}
|
|
<span class="side-version side-label">v{{ version }}</span>
|
|
{% if not is_admin %}
|
|
<button type="button" class="theme-toggle-foot" id="theme-toggle" title="Basculer entre thème sombre et clair" aria-label="Basculer entre thème sombre et clair"><span data-theme-ico>☀️</span></button>
|
|
{% endif %}
|
|
{% if auth_enabled %}
|
|
<a href="/logout" class="side-logout" title="Se déconnecter">⏻<span class="side-label"> Sortir</span></a>
|
|
{% endif %}
|
|
</div>
|
|
</aside>
|
|
<div class="side-backdrop" id="side-backdrop" hidden></div>
|
|
<header class="app-header topbar">
|
|
<button type="button" class="nav-burger" id="nav-burger" aria-expanded="false" aria-controls="sidebar" aria-label="Ouvrir le menu">☰</button>
|
|
{% if owner is defined and repo is defined %}
|
|
<span class="crumb"><a href="/board/{{ owner }}/{{ repo }}">{{ owner }} / <strong>{{ repo }}</strong></a></span>
|
|
{% elif full_name is defined %}
|
|
<span class="crumb"><a href="/board/{{ full_name }}">{{ full_name }}</a></span>
|
|
{% endif %}
|
|
<div class="header-actions">
|
|
{% if repo_info is defined and repo_info and repo_info.html_url %}
|
|
<a href="{{ repo_info.html_url }}" target="_blank" rel="noopener" class="nav-pill" title="Ouvrir ce dépôt dans Gitea">🔗 Gitea ↗</a>
|
|
{% endif %}
|
|
</div>
|
|
<script src="/static/js/nav.js?v={{ version }}" defer></script>
|
|
</header>
|