Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4b3b324f5 | ||
|
|
d395642f16 | ||
|
|
ec3590d25f | ||
|
|
16f03184bf | ||
|
|
6fdf39959c | ||
|
|
18e1dd0f3d | ||
|
|
58204e621e | ||
|
|
2fde103e93 |
@@ -1,5 +1,52 @@
|
||||
# Changelog
|
||||
|
||||
## 0.16.0 — 2026-10-11
|
||||
|
||||
### Nouveautés
|
||||
- **Layout repensé** (issue #16, retour de Bruno sur la 0.15.0 :
|
||||
disposition pas conviviale, trop de place, mobile inchangé) —
|
||||
la barre de pilules horizontale disparaît au profit d'une
|
||||
**barre latérale** : navigation groupée (Principal, Rituels,
|
||||
Pilotage, Outils), bouton palette en tête, **repliable en rail
|
||||
d'icônes** sur desktop (état mémorisé par navigateur), et
|
||||
**tiroir hors-canvas** sur mobile (burger dans une topbar mince
|
||||
de 52 px : fil d'Ariane + actions contextuelles seulement).
|
||||
Le partiel `_header.html` reste l'unique source du cadre.
|
||||
- **Wallboard** : hauteur verrouillée à l'écran (`100dvh`,
|
||||
`overflow: hidden`) — **plus aucune barre de défilement de
|
||||
page** ; le pied de diapositive (« ✅ Fermées aujourd'hui »,
|
||||
« 📋 Ouvertes », « ● EN DIRECT ») reste **toujours visible**,
|
||||
au-dessus de la zone réservée à la navigation des diapos ; la
|
||||
liste des cartes se tronque avec un fondu et une ligne
|
||||
« … +N autres cartes en vol » ; mode compact sous 760 px de
|
||||
hauteur (TV 720p).
|
||||
|
||||
6 tests adaptés/ajoutés (171 au total).
|
||||
|
||||
## 0.15.0 — 2026-10-11
|
||||
|
||||
### Nouveautés
|
||||
- **Header refait** (issue #16, créée et spécifiée par Bruno) :
|
||||
les 19 copies de markup de header sont remplacées par un
|
||||
**partiel unique `_header.html`**. Desktop : une seule ligne —
|
||||
5 entrées primaires (Projets, Focus, Aujourd'hui, Recherche,
|
||||
Capture), menu « Plus ▾ » pour les 9 autres, Ctrl+K à droite,
|
||||
page active marquée (y compris dans « Plus »). Mobile
|
||||
(≤ 900 px) : burger + panneau plein largeur, cibles ≥ 44 px,
|
||||
fermeture Échap / clic extérieur. La page active est déduite du
|
||||
gabarit dans `render()` (`active_nav`) — une seule source de
|
||||
vérité. Le wallboard garde son plein écran sans header et la
|
||||
session triage utilise le même partiel. `static/js/nav.js`
|
||||
partagé, aucune dépendance ajoutée.
|
||||
|
||||
### Corrigé
|
||||
- Prévisions : les semaines se calent désormais sur le référentiel
|
||||
du journal (UTC, comme les horodatages `board_events`) — une
|
||||
fermeture journalisée en soirée locale (déjà le lendemain en
|
||||
UTC) tombait hors de toutes les semaines et faussait le débit.
|
||||
|
||||
5 tests ajoutés (170 au total).
|
||||
|
||||
## 0.14.0 — 2026-10-11
|
||||
|
||||
### Nouveautés
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ from pathlib import Path
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
APP_VERSION = "0.14.0"
|
||||
APP_VERSION = "0.16.0"
|
||||
|
||||
|
||||
def _env_bool(name: str, default: bool) -> bool:
|
||||
|
||||
+1
-1
@@ -143,7 +143,7 @@ async def _mcp_session(client: httpx.AsyncClient, url: str, token: str) -> None:
|
||||
{
|
||||
"protocolVersion": MCP_PROTOCOL_VERSION,
|
||||
"capabilities": {},
|
||||
"clientInfo": {"name": "kanbante", "version": "0.14.0"},
|
||||
"clientInfo": {"name": "kanbante", "version": "0.16.0"},
|
||||
},
|
||||
req_id=1,
|
||||
)
|
||||
|
||||
+11
-3
@@ -7,7 +7,15 @@ date, and are withheld entirely when the journal has no closures
|
||||
to learn from — "not enough history" is an answer, too.
|
||||
"""
|
||||
|
||||
from datetime import date, datetime, timedelta
|
||||
from datetime import date, datetime, timedelta, timezone
|
||||
|
||||
|
||||
def _journal_today() -> date:
|
||||
"""'Today' in the journal's frame: board_events stamps are UTC
|
||||
(SQLite datetime('now')), so week buckets anchor to UTC —
|
||||
otherwise closures logged in the local evening (already
|
||||
tomorrow in UTC) fall outside every bucket."""
|
||||
return datetime.now(timezone.utc).date()
|
||||
|
||||
MONTHS_FR = [
|
||||
"janvier", "février", "mars", "avril", "mai", "juin",
|
||||
@@ -35,7 +43,7 @@ def _event_date(event: dict) -> date | None:
|
||||
def weekly_closures(events: list[dict], weeks: int = 8,
|
||||
today: date | None = None) -> list[dict]:
|
||||
"""Closure counts per ISO week, oldest first, current week last."""
|
||||
today = today or date.today()
|
||||
today = today or _journal_today()
|
||||
current_monday = today - timedelta(days=today.weekday())
|
||||
buckets: list[dict] = []
|
||||
for i in range(weeks - 1, -1, -1):
|
||||
@@ -83,7 +91,7 @@ def estimate(remaining: int, weekly: list[dict],
|
||||
no throughput to project from, else dates: central (avg rate),
|
||||
earliest (best week), latest (worst nonzero week, if any).
|
||||
"""
|
||||
today = today or date.today()
|
||||
today = today or _journal_today()
|
||||
if remaining <= 0:
|
||||
return {"done": True}
|
||||
stats = throughput_stats(weekly)
|
||||
|
||||
+23
@@ -292,6 +292,27 @@ _tpl_env = Environment(
|
||||
)
|
||||
|
||||
|
||||
# Which main-nav entry a template represents (issue #16): the
|
||||
# shared _header.html partial highlights it from this one place
|
||||
# instead of 19 hand-maintained copies.
|
||||
_NAV_BY_TEMPLATE = {
|
||||
"dashboard.html": "home",
|
||||
"focus.html": "focus",
|
||||
"today.html": "today",
|
||||
"my.html": "my",
|
||||
"activity.html": "activity",
|
||||
"briefing.html": "briefing",
|
||||
"standup.html": "standup",
|
||||
"search.html": "search",
|
||||
"capture.html": "capture",
|
||||
"digest.html": "digest",
|
||||
"forecast.html": "forecast",
|
||||
"rules.html": "rules",
|
||||
"health.html": "health",
|
||||
"health_project.html": "health",
|
||||
}
|
||||
|
||||
|
||||
def render(
|
||||
request: Request, name: str, status_code: int = 200, **context
|
||||
) -> HTMLResponse:
|
||||
@@ -307,6 +328,7 @@ def render(
|
||||
token = secrets.token_urlsafe(32)
|
||||
context.setdefault("csrf_token", token)
|
||||
context.setdefault("version", APP_VERSION)
|
||||
context.setdefault("active_nav", _NAV_BY_TEMPLATE.get(name, ""))
|
||||
html = _tpl_env.get_template(name).render(**context)
|
||||
response = HTMLResponse(html, status_code=status_code)
|
||||
if generated:
|
||||
@@ -1535,6 +1557,7 @@ async def _collect_wall_data(only_repo: str | None) -> list[dict]:
|
||||
"counts": {col: len(columns[col]) for col in KANBAN_COLUMNS},
|
||||
"open_total": len(open_issues),
|
||||
"cards": cards[:9],
|
||||
"cards_total": len(cards),
|
||||
"closed_today": closed_today.get(full_name, 0),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
{# ── 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. #}
|
||||
<script>
|
||||
document.body.classList.add('with-sidebar');
|
||||
try {
|
||||
if (localStorage.getItem('kanbante-sidebar') === 'collapsed') {
|
||||
document.body.classList.add('sidebar-collapsed');
|
||||
}
|
||||
} catch (e) { /* stockage indisponible : état par défaut */ }
|
||||
</script>
|
||||
{% set nav_groups = [
|
||||
("Principal", [
|
||||
("home", "/", "🏠", "Projets"),
|
||||
("focus", "/focus", "🎯", "Focus"),
|
||||
("today", "/today", "🌅", "Aujourd'hui"),
|
||||
("my", "/my", "👤", "Mes issues"),
|
||||
]),
|
||||
("Rituels", [
|
||||
("briefing", "/briefing", "📊", "Briefing"),
|
||||
("standup", "/standup", "🗣️", "Standup"),
|
||||
("digest", "/digest", "🧠", "Digest"),
|
||||
]),
|
||||
("Pilotage", [
|
||||
("forecast", "/forecast", "🔮", "Prévisions"),
|
||||
("health", "/health", "🩺", "Santé"),
|
||||
("rules", "/rules", "⚙️", "Règles"),
|
||||
("activity", "/activity", "📰", "Activité"),
|
||||
]),
|
||||
("Outils", [
|
||||
("search", "/search", "🔍", "Recherche"),
|
||||
("capture", "/capture", "⚡", "Capture"),
|
||||
("wall", "/wall", "🖥️", "Wallboard"),
|
||||
]),
|
||||
] %}
|
||||
<aside class="sidebar" id="sidebar" aria-label="Navigation principale">
|
||||
<a href="/" class="side-logo" aria-label="Kanbante — accueil">
|
||||
<span class="logo-mark">📋</span>
|
||||
<span class="side-label gradient-text">Kanbante</span>
|
||||
</a>
|
||||
<button type="button" class="side-search palette-trigger" title="Palette de commandes (Ctrl+K)">
|
||||
<span class="side-ico">⌨️</span>
|
||||
<span class="side-label">Recherche & commandes</span>
|
||||
<kbd class="side-label">Ctrl K</kbd>
|
||||
</button>
|
||||
<nav class="side-nav">
|
||||
{% for group, links in nav_groups %}
|
||||
<div class="side-group">{{ group }}</div>
|
||||
{% 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">{{ icon }}</span>
|
||||
<span class="side-label">{{ label }}</span>
|
||||
</a>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</nav>
|
||||
<div class="side-foot">
|
||||
<button type="button" class="side-collapse" id="side-collapse" aria-label="Replier ou déplier le menu">
|
||||
<span class="side-collapse-ico">«</span>
|
||||
<span class="side-label">Replier</span>
|
||||
</button>
|
||||
<span class="side-version side-label">v{{ version }}</span>
|
||||
</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 %}
|
||||
<button type="button" class="nav-pill palette-trigger topbar-search" title="Recherche et commandes (Ctrl+K)">🔍</button>
|
||||
</div>
|
||||
<script src="/static/js/nav.js" defer></script>
|
||||
</header>
|
||||
@@ -8,29 +8,7 @@
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<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>
|
||||
<span class="crumb"><span class="sep">/</span> 📰 <strong>Activité</strong></span>
|
||||
</div>
|
||||
<nav class="nav-pills">
|
||||
<a href="/" class="nav-pill">🏠 Projets</a>
|
||||
<a href="/focus" class="nav-pill">🎯 Focus</a>
|
||||
<a href="/today" class="nav-pill">🌅 Aujourd'hui</a>
|
||||
<a href="/my" class="nav-pill">👤 Mes issues</a>
|
||||
<a href="/activity" class="nav-pill active">📰 Activité</a>
|
||||
<a href="/briefing" class="nav-pill">📊 Briefing</a>
|
||||
<a href="/standup" class="nav-pill">🗣️ Standup</a>
|
||||
<a href="/search" class="nav-pill">🔍 Recherche</a>
|
||||
<a href="/capture" class="nav-pill">⚡ Capture</a>
|
||||
<a href="/digest" class="nav-pill">🧠 Digest</a>
|
||||
<a href="/forecast" class="nav-pill">🔮 Prévisions</a>
|
||||
<a href="/rules" class="nav-pill">⚙️ Règles</a>
|
||||
<a href="/health" class="nav-pill">🩺 Santé</a>
|
||||
<a href="/wall" class="nav-pill">🖥️ Wallboard</a>
|
||||
<button type="button" class="nav-pill palette-trigger" title="Palette de commandes (Ctrl+K)">⌨️ Ctrl K</button>
|
||||
</nav>
|
||||
</header>
|
||||
{% include "_header.html" %}
|
||||
|
||||
<main class="focus-main">
|
||||
<p class="focus-intro">Les {{ events|length }} derniers événements du journal — déplacements, créations, fermetures et commentaires, tous projets confondus.</p>
|
||||
|
||||
@@ -12,30 +12,7 @@
|
||||
<script src="/static/js/board.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<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>
|
||||
<span class="crumb"><span class="sep">/</span> {{ owner }} / <strong>{{ repo }}</strong></span>
|
||||
</div>
|
||||
<nav class="nav-pills">
|
||||
<a href="/" class="nav-pill">🏠 Projets</a>
|
||||
<a href="/focus" class="nav-pill">🎯 Focus</a>
|
||||
<a href="/today" class="nav-pill">🌅 Aujourd'hui</a>
|
||||
<a href="/my" class="nav-pill">👤 Mes issues</a>
|
||||
<a href="/activity" class="nav-pill">📰 Activité</a>
|
||||
<a href="/search" class="nav-pill">🔍 Recherche</a>
|
||||
<a href="/capture" class="nav-pill">⚡ Capture</a>
|
||||
<a href="/digest" class="nav-pill">🧠 Digest</a>
|
||||
<a href="/forecast" class="nav-pill">🔮 Prévisions</a>
|
||||
<a href="/rules" class="nav-pill">⚙️ Règles</a>
|
||||
<a href="/health" class="nav-pill">🩺 Santé</a>
|
||||
<a href="/wall" class="nav-pill">🖥️ Wallboard</a>
|
||||
<button type="button" class="nav-pill palette-trigger" title="Palette de commandes (Ctrl+K)">⌨️ Ctrl K</button>
|
||||
{% if repo_info.html_url %}
|
||||
<a href="{{ repo_info.html_url }}" target="_blank" class="nav-pill">🔗 Gitea ↗</a>
|
||||
{% endif %}
|
||||
</nav>
|
||||
</header>
|
||||
{% include "_header.html" %}
|
||||
|
||||
{% if milestones %}
|
||||
<div class="milestone-bar">
|
||||
|
||||
@@ -9,27 +9,7 @@
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<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>
|
||||
<span class="crumb"><span class="sep">/</span> 📊 <strong>Briefing</strong></span>
|
||||
</div>
|
||||
<nav class="nav-pills">
|
||||
<a href="/" class="nav-pill">🏠 Projets</a>
|
||||
<a href="/focus" class="nav-pill">🎯 Focus</a>
|
||||
<a href="/today" class="nav-pill">🌅 Aujourd'hui</a>
|
||||
<a href="/my" class="nav-pill">👤 Mes issues</a>
|
||||
<a href="/activity" class="nav-pill">📰 Activité</a>
|
||||
<a href="/search" class="nav-pill">🔍 Recherche</a>
|
||||
<a href="/capture" class="nav-pill">⚡ Capture</a>
|
||||
<a href="/digest" class="nav-pill">🧠 Digest</a>
|
||||
<a href="/forecast" class="nav-pill">🔮 Prévisions</a>
|
||||
<a href="/rules" class="nav-pill">⚙️ Règles</a>
|
||||
<a href="/health" class="nav-pill">🩺 Santé</a>
|
||||
<a href="/wall" class="nav-pill">🖥️ Wallboard</a>
|
||||
<button type="button" class="nav-pill palette-trigger" title="Palette de commandes (Ctrl+K)">⌨️ Ctrl K</button>
|
||||
</nav>
|
||||
</header>
|
||||
{% include "_header.html" %}
|
||||
|
||||
<main class="focus-main">
|
||||
<p class="focus-intro">Le briefing du <strong>{{ data.date }}</strong> — le même contenu que celui poussé sur ntfy.</p>
|
||||
|
||||
@@ -8,26 +8,7 @@
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="app-header">
|
||||
<a href="/" class="logo">📋 Kanbante</a>
|
||||
<nav class="nav-pills">
|
||||
<a href="/" class="nav-pill">🏠 Projets</a>
|
||||
<a href="/focus" class="nav-pill">🎯 Focus</a>
|
||||
<a href="/today" class="nav-pill">🌅 Aujourd'hui</a>
|
||||
<a href="/my" class="nav-pill">👤 Mes issues</a>
|
||||
<a href="/activity" class="nav-pill">📰 Activité</a>
|
||||
<a href="/briefing" class="nav-pill">📊 Briefing</a>
|
||||
<a href="/standup" class="nav-pill">🗣️ Standup</a>
|
||||
<a href="/search" class="nav-pill">🔍 Recherche</a>
|
||||
<a href="/capture" class="nav-pill active">⚡ Capture</a>
|
||||
<a href="/digest" class="nav-pill">🧠 Digest</a>
|
||||
<a href="/forecast" class="nav-pill">🔮 Prévisions</a>
|
||||
<a href="/rules" class="nav-pill">⚙️ Règles</a>
|
||||
<a href="/health" class="nav-pill">🩺 Santé</a>
|
||||
<a href="/wall" class="nav-pill">🖥️ Wallboard</a>
|
||||
<button type="button" class="nav-pill palette-trigger" title="Palette de commandes (Ctrl+K)">⌨️ Ctrl K</button>
|
||||
</nav>
|
||||
</header>
|
||||
{% include "_header.html" %}
|
||||
|
||||
<main class="focus-main">
|
||||
<section class="briefing-card">
|
||||
|
||||
@@ -9,28 +9,7 @@
|
||||
<script defer src="/static/vendor/alpine.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<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="/today" class="nav-pill">🌅 Aujourd'hui</a>
|
||||
<a href="/my" class="nav-pill">👤 Mes issues</a>
|
||||
<a href="/activity" class="nav-pill">📰 Activité</a>
|
||||
<a href="/briefing" class="nav-pill">📊 Briefing</a>
|
||||
<a href="/standup" class="nav-pill">🗣️ Standup</a>
|
||||
<a href="/search" class="nav-pill">🔍 Recherche</a>
|
||||
<a href="/capture" class="nav-pill">⚡ Capture</a>
|
||||
<a href="/digest" class="nav-pill">🧠 Digest</a>
|
||||
<a href="/forecast" class="nav-pill">🔮 Prévisions</a>
|
||||
<a href="/rules" class="nav-pill">⚙️ Règles</a>
|
||||
<a href="/health" class="nav-pill">🩺 Santé</a>
|
||||
<a href="/wall" class="nav-pill">🖥️ Wallboard</a>
|
||||
<button type="button" class="nav-pill palette-trigger" title="Palette de commandes (Ctrl+K)">⌨️ Ctrl K</button>
|
||||
</nav>
|
||||
</header>
|
||||
{% include "_header.html" %}
|
||||
|
||||
<section class="hero">
|
||||
<div class="hero-text">
|
||||
|
||||
@@ -8,23 +8,7 @@
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="app-header">
|
||||
<a href="/" class="logo">📋 Kanbante</a>
|
||||
<nav class="nav-pills">
|
||||
<a href="/" class="nav-pill">🏠 Projets</a>
|
||||
<a href="/focus" class="nav-pill">🎯 Focus</a>
|
||||
<a href="/today" class="nav-pill">🌅 Aujourd'hui</a>
|
||||
<a href="/my" class="nav-pill">👤 Mes issues</a>
|
||||
<a href="/activity" class="nav-pill">📰 Activité</a>
|
||||
<a href="/briefing" class="nav-pill">📊 Briefing</a>
|
||||
<a href="/standup" class="nav-pill">🗣️ Standup</a>
|
||||
<a href="/search" class="nav-pill">🔍 Recherche</a>
|
||||
<a href="/capture" class="nav-pill">⚡ Capture</a>
|
||||
<a href="/digest" class="nav-pill active">🧠 Digest</a>
|
||||
<a href="/wall" class="nav-pill">🖥️ Wallboard</a>
|
||||
<button type="button" class="nav-pill palette-trigger" title="Palette de commandes (Ctrl+K)">⌨️ Ctrl K</button>
|
||||
</nav>
|
||||
</header>
|
||||
{% include "_header.html" %}
|
||||
|
||||
<main class="focus-main">
|
||||
<section class="briefing-card">
|
||||
|
||||
@@ -7,18 +7,7 @@
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<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">🏠 Projets</a>
|
||||
<a href="/focus" class="nav-pill">🎯 Focus</a>
|
||||
<a href="/my" class="nav-pill">👤 Mes issues</a>
|
||||
<a href="/activity" class="nav-pill">📰 Activité</a>
|
||||
<a href="/search" class="nav-pill">🔍 Recherche</a>
|
||||
</nav>
|
||||
</header>
|
||||
{% include "_header.html" %}
|
||||
<main class="error-main">
|
||||
<div class="error-box">
|
||||
<h2>⚠️ Quelque chose s'est mal passé</h2>
|
||||
|
||||
@@ -8,29 +8,7 @@
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<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>
|
||||
<span class="crumb"><span class="sep">/</span> 🎯 <strong>Focus</strong></span>
|
||||
</div>
|
||||
<nav class="nav-pills">
|
||||
<a href="/" class="nav-pill">🏠 Projets</a>
|
||||
<a href="/focus" class="nav-pill active">🎯 Focus</a>
|
||||
<a href="/my" class="nav-pill">👤 Mes issues</a>
|
||||
<a href="/activity" class="nav-pill">📰 Activité</a>
|
||||
<a href="/briefing" class="nav-pill">📊 Briefing</a>
|
||||
<a href="/standup" class="nav-pill">🗣️ Standup</a>
|
||||
<a href="/search" class="nav-pill">🔍 Recherche</a>
|
||||
<a href="/capture" class="nav-pill">⚡ Capture</a>
|
||||
<a href="/digest" class="nav-pill">🧠 Digest</a>
|
||||
<a href="/forecast" class="nav-pill">🔮 Prévisions</a>
|
||||
<a href="/rules" class="nav-pill">⚙️ Règles</a>
|
||||
<a href="/health" class="nav-pill">🩺 Santé</a>
|
||||
<a href="/wall" class="nav-pill">🖥️ Wallboard</a>
|
||||
<button type="button" class="nav-pill palette-trigger" title="Palette de commandes (Ctrl+K)">⌨️ Ctrl K</button>
|
||||
<span class="badge">{{ groups|map(attribute='cards')|map('length')|sum }} cartes en cours</span>
|
||||
</nav>
|
||||
</header>
|
||||
{% include "_header.html" %}
|
||||
|
||||
<main class="focus-main">
|
||||
<p class="focus-intro">Tout ce qui est <strong>En cours</strong> ou <strong>En révision</strong>, tous projets actifs confondus.</p>
|
||||
|
||||
@@ -8,24 +8,7 @@
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="app-header">
|
||||
<a href="/" class="logo">📋 Kanbante</a>
|
||||
<nav class="nav-pills">
|
||||
<a href="/" class="nav-pill">🏠 Projets</a>
|
||||
<a href="/focus" class="nav-pill">🎯 Focus</a>
|
||||
<a href="/today" class="nav-pill">🌅 Aujourd'hui</a>
|
||||
<a href="/my" class="nav-pill">👤 Mes issues</a>
|
||||
<a href="/activity" class="nav-pill">📰 Activité</a>
|
||||
<a href="/briefing" class="nav-pill">📊 Briefing</a>
|
||||
<a href="/standup" class="nav-pill">🗣️ Standup</a>
|
||||
<a href="/search" class="nav-pill">🔍 Recherche</a>
|
||||
<a href="/capture" class="nav-pill">⚡ Capture</a>
|
||||
<a href="/digest" class="nav-pill">🧠 Digest</a>
|
||||
<a href="/forecast" class="nav-pill active">🔮 Prévisions</a>
|
||||
<a href="/wall" class="nav-pill">🖥️ Wallboard</a>
|
||||
<button type="button" class="nav-pill palette-trigger" title="Palette de commandes (Ctrl+K)">⌨️ Ctrl K</button>
|
||||
</nav>
|
||||
</header>
|
||||
{% include "_header.html" %}
|
||||
|
||||
<main class="focus-main">
|
||||
<section class="briefing-card">
|
||||
|
||||
@@ -8,19 +8,7 @@
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="app-header">
|
||||
<a href="/" class="logo">📋 Kanbante</a>
|
||||
<nav class="nav-pills">
|
||||
<a href="/" class="nav-pill">🏠 Projets</a>
|
||||
<a href="/focus" class="nav-pill">🎯 Focus</a>
|
||||
<a href="/today" class="nav-pill">🌅 Aujourd'hui</a>
|
||||
<a href="/activity" class="nav-pill">📰 Activité</a>
|
||||
<a href="/forecast" class="nav-pill">🔮 Prévisions</a>
|
||||
<a href="/rules" class="nav-pill">⚙️ Règles</a>
|
||||
<a href="/health" class="nav-pill active">🩺 Santé</a>
|
||||
<button type="button" class="nav-pill palette-trigger" title="Palette de commandes (Ctrl+K)">⌨️ Ctrl K</button>
|
||||
</nav>
|
||||
</header>
|
||||
{% include "_header.html" %}
|
||||
|
||||
<main class="focus-main">
|
||||
<section class="briefing-card">
|
||||
|
||||
@@ -8,15 +8,7 @@
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</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="/health" class="nav-pill active">🩺 Santé</a>
|
||||
<a href="/forecast" class="nav-pill">🔮 Prévisions</a>
|
||||
<button type="button" class="nav-pill palette-trigger" title="Palette de commandes (Ctrl+K)">⌨️ Ctrl K</button>
|
||||
</nav>
|
||||
</header>
|
||||
{% include "_header.html" %}
|
||||
|
||||
<main class="focus-main">
|
||||
{% if health is none %}
|
||||
|
||||
+1
-21
@@ -8,27 +8,7 @@
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<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>
|
||||
<span class="crumb"><span class="sep">/</span> 👤 <strong>Mes issues</strong></span>
|
||||
</div>
|
||||
<nav class="nav-pills">
|
||||
<a href="/" class="nav-pill">🏠 Projets</a>
|
||||
<a href="/focus" class="nav-pill">🎯 Focus</a>
|
||||
<a href="/today" class="nav-pill">🌅 Aujourd'hui</a>
|
||||
<a href="/my" class="nav-pill active">👤 Mes issues</a>
|
||||
<a href="/activity" class="nav-pill">📰 Activité</a>
|
||||
<a href="/search" class="nav-pill">🔍 Recherche</a>
|
||||
<a href="/capture" class="nav-pill">⚡ Capture</a>
|
||||
<a href="/digest" class="nav-pill">🧠 Digest</a>
|
||||
<a href="/forecast" class="nav-pill">🔮 Prévisions</a>
|
||||
<a href="/rules" class="nav-pill">⚙️ Règles</a>
|
||||
<a href="/health" class="nav-pill">🩺 Santé</a>
|
||||
<a href="/wall" class="nav-pill">🖥️ Wallboard</a>
|
||||
<button type="button" class="nav-pill palette-trigger" title="Palette de commandes (Ctrl+K)">⌨️ Ctrl K</button>
|
||||
</nav>
|
||||
</header>
|
||||
{% include "_header.html" %}
|
||||
|
||||
<main class="focus-main">
|
||||
{% if not user %}
|
||||
|
||||
@@ -8,18 +8,7 @@
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="app-header">
|
||||
<a href="/" class="logo">📋 Kanbante</a>
|
||||
<nav class="nav-pills">
|
||||
<a href="/" class="nav-pill">🏠 Projets</a>
|
||||
<a href="/focus" class="nav-pill">🎯 Focus</a>
|
||||
<a href="/today" class="nav-pill">🌅 Aujourd'hui</a>
|
||||
<a href="/activity" class="nav-pill">📰 Activité</a>
|
||||
<a href="/forecast" class="nav-pill">🔮 Prévisions</a>
|
||||
<a href="/rules" class="nav-pill active">⚙️ Règles</a>
|
||||
<button type="button" class="nav-pill palette-trigger" title="Palette de commandes (Ctrl+K)">⌨️ Ctrl K</button>
|
||||
</nav>
|
||||
</header>
|
||||
{% include "_header.html" %}
|
||||
|
||||
<main class="focus-main">
|
||||
<section class="briefing-card">
|
||||
|
||||
@@ -8,27 +8,7 @@
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<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>
|
||||
<span class="crumb"><span class="sep">/</span> 🔍 <strong>Recherche</strong></span>
|
||||
</div>
|
||||
<nav class="nav-pills">
|
||||
<a href="/" class="nav-pill">🏠 Projets</a>
|
||||
<a href="/focus" class="nav-pill">🎯 Focus</a>
|
||||
<a href="/today" class="nav-pill">🌅 Aujourd'hui</a>
|
||||
<a href="/my" class="nav-pill">👤 Mes issues</a>
|
||||
<a href="/activity" class="nav-pill">📰 Activité</a>
|
||||
<a href="/search" class="nav-pill active">🔍 Recherche</a>
|
||||
<a href="/capture" class="nav-pill">⚡ Capture</a>
|
||||
<a href="/digest" class="nav-pill">🧠 Digest</a>
|
||||
<a href="/forecast" class="nav-pill">🔮 Prévisions</a>
|
||||
<a href="/rules" class="nav-pill">⚙️ Règles</a>
|
||||
<a href="/health" class="nav-pill">🩺 Santé</a>
|
||||
<a href="/wall" class="nav-pill">🖥️ Wallboard</a>
|
||||
<button type="button" class="nav-pill palette-trigger" title="Palette de commandes (Ctrl+K)">⌨️ Ctrl K</button>
|
||||
</nav>
|
||||
</header>
|
||||
{% include "_header.html" %}
|
||||
|
||||
<main class="focus-main">
|
||||
<form method="get" action="/search" class="search-hero">
|
||||
|
||||
@@ -10,17 +10,7 @@
|
||||
<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>
|
||||
<a href="/health" class="nav-pill">🩺 Santé</a>
|
||||
<button type="button" class="nav-pill palette-trigger" title="Palette de commandes (Ctrl+K)">⌨️ Ctrl K</button>
|
||||
</nav>
|
||||
</header>
|
||||
{% include "_header.html" %}
|
||||
|
||||
<main class="focus-main">
|
||||
{% if markdown %}
|
||||
|
||||
@@ -8,17 +8,7 @@
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="app-header">
|
||||
<a href="/" class="logo">📋 Kanbante</a>
|
||||
<nav class="nav-pills">
|
||||
<a href="/" class="nav-pill">🏠 Projets</a>
|
||||
<a href="/focus" class="nav-pill">🎯 Focus</a>
|
||||
<a href="/today" class="nav-pill">🌅 Aujourd'hui</a>
|
||||
<a href="/briefing" class="nav-pill">📊 Briefing</a>
|
||||
<a href="/standup" class="nav-pill">🗣️ Standup</a>
|
||||
<button type="button" class="nav-pill palette-trigger" title="Palette de commandes (Ctrl+K)">⌨️ Ctrl K</button>
|
||||
</nav>
|
||||
</header>
|
||||
{% include "_header.html" %}
|
||||
|
||||
<main class="focus-main">
|
||||
<section class="briefing-card">
|
||||
|
||||
@@ -8,26 +8,7 @@
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="app-header">
|
||||
<a href="/" class="logo">📋 Kanbante</a>
|
||||
<nav class="nav-pills">
|
||||
<a href="/" class="nav-pill">🏠 Projets</a>
|
||||
<a href="/focus" class="nav-pill">🎯 Focus</a>
|
||||
<a href="/today" class="nav-pill active">🌅 Aujourd'hui</a>
|
||||
<a href="/my" class="nav-pill">👤 Mes issues</a>
|
||||
<a href="/activity" class="nav-pill">📰 Activité</a>
|
||||
<a href="/briefing" class="nav-pill">📊 Briefing</a>
|
||||
<a href="/standup" class="nav-pill">🗣️ Standup</a>
|
||||
<a href="/search" class="nav-pill">🔍 Recherche</a>
|
||||
<a href="/capture" class="nav-pill">⚡ Capture</a>
|
||||
<a href="/digest" class="nav-pill">🧠 Digest</a>
|
||||
<a href="/forecast" class="nav-pill">🔮 Prévisions</a>
|
||||
<a href="/rules" class="nav-pill">⚙️ Règles</a>
|
||||
<a href="/health" class="nav-pill">🩺 Santé</a>
|
||||
<a href="/wall" class="nav-pill">🖥️ Wallboard</a>
|
||||
<button type="button" class="nav-pill palette-trigger" title="Palette de commandes (Ctrl+K)">⌨️ Ctrl K</button>
|
||||
</nav>
|
||||
</header>
|
||||
{% include "_header.html" %}
|
||||
|
||||
<main class="today-main">
|
||||
<h1 class="today-title">🌅 {{ date_label }}</h1>
|
||||
|
||||
@@ -8,26 +8,7 @@
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="app-header">
|
||||
<a href="/" class="logo">📋 Kanbante</a>
|
||||
<nav class="nav-pills">
|
||||
<a href="/" class="nav-pill">🏠 Projets</a>
|
||||
<a href="/focus" class="nav-pill">🎯 Focus</a>
|
||||
<a href="/today" class="nav-pill">🌅 Aujourd'hui</a>
|
||||
<a href="/my" class="nav-pill">👤 Mes issues</a>
|
||||
<a href="/activity" class="nav-pill">📰 Activité</a>
|
||||
<a href="/briefing" class="nav-pill">📊 Briefing</a>
|
||||
<a href="/standup" class="nav-pill">🗣️ Standup</a>
|
||||
<a href="/search" class="nav-pill">🔍 Recherche</a>
|
||||
<a href="/capture" class="nav-pill">⚡ Capture</a>
|
||||
<a href="/digest" class="nav-pill">🧠 Digest</a>
|
||||
<a href="/forecast" class="nav-pill">🔮 Prévisions</a>
|
||||
<a href="/rules" class="nav-pill">⚙️ Règles</a>
|
||||
<a href="/health" class="nav-pill">🩺 Santé</a>
|
||||
<a href="/wall" class="nav-pill">🖥️ Wallboard</a>
|
||||
<button type="button" class="nav-pill palette-trigger" title="Palette de commandes (Ctrl+K)">⌨️ Ctrl K</button>
|
||||
</nav>
|
||||
</header>
|
||||
{% include "_header.html" %}
|
||||
|
||||
<main class="focus-main">
|
||||
<section class="briefing-card">
|
||||
|
||||
@@ -8,14 +8,7 @@
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</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="/triage" class="nav-pill">📥 Autre projet</a>
|
||||
<button type="button" class="nav-pill palette-trigger" title="Palette de commandes (Ctrl+K)">⌨️ Ctrl K</button>
|
||||
</nav>
|
||||
</header>
|
||||
{% include "_header.html" %}
|
||||
|
||||
<main class="triage-main" data-repo="{{ full_name }}">
|
||||
<div class="triage-head">
|
||||
|
||||
@@ -45,6 +45,9 @@
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% if slide.cards_total > slide.cards|length %}
|
||||
<li class="wall-card wall-more">… +{{ slide.cards_total - slide.cards|length }} autres cartes en vol</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="wall-idle">🌿 Rien en vol pour le moment — tout est au Backlog ou terminé.</p>
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "kanbante"
|
||||
version = "0.14.0"
|
||||
version = "0.16.0"
|
||||
description = "Kanban project manager integrated with Gitea"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
|
||||
+124
-5
@@ -900,15 +900,21 @@ footer {
|
||||
color: #e5ecf5;
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
overflow: hidden;
|
||||
}
|
||||
.wall-stage { position: relative; min-height: 100vh; }
|
||||
.wall-stage { position: relative; height: 100vh; height: 100dvh; }
|
||||
.wall-slide {
|
||||
display: none;
|
||||
min-height: 100vh;
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
max-height: 100dvh;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
padding: 4vh 4vw;
|
||||
padding: 3.5vh 4vw 96px;
|
||||
flex-direction: column;
|
||||
gap: 3vh;
|
||||
gap: 2.5vh;
|
||||
}
|
||||
.wall-slide.active { display: flex; animation: wall-in 0.45s ease; }
|
||||
@keyframes wall-in { from { opacity: 0; } to { opacity: 1; } }
|
||||
@@ -934,7 +940,14 @@ footer {
|
||||
}
|
||||
.wall-count-n { display: block; font-size: clamp(1.9rem, 3.6vw, 3rem); font-weight: 800; }
|
||||
.wall-count-l { display: block; color: #aebdd2; font-size: clamp(0.78rem, 1.25vw, 1.02rem); margin-top: 2px; }
|
||||
.wall-cards { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
|
||||
.wall-cards {
|
||||
list-style: none; margin: 0; padding: 0;
|
||||
display: flex; flex-direction: column; gap: 12px;
|
||||
flex: 1 1 auto; min-height: 0; overflow: hidden;
|
||||
-webkit-mask-image: linear-gradient(to bottom, #000 88%, transparent);
|
||||
mask-image: linear-gradient(to bottom, #000 88%, transparent);
|
||||
}
|
||||
.wall-more { justify-content: center; color: #8fa3bf; font-size: 1.1rem; background: transparent; }
|
||||
.wall-card {
|
||||
display: flex; align-items: center; gap: 18px;
|
||||
background: rgba(255, 255, 255, 0.045);
|
||||
@@ -954,8 +967,17 @@ footer {
|
||||
.wall-foot {
|
||||
margin-top: auto;
|
||||
display: flex; gap: 36px; flex-wrap: wrap;
|
||||
justify-content: space-between; align-items: center;
|
||||
width: 100%; box-sizing: border-box;
|
||||
font-size: clamp(1rem, 1.7vw, 1.35rem); color: #aebdd2;
|
||||
}
|
||||
@media (max-height: 760px) {
|
||||
.wall-slide { gap: 1.6vh; padding-top: 2.5vh; }
|
||||
.wall-card { padding: 10px 16px; }
|
||||
.wall-count { padding: 8px 8px 7px; }
|
||||
.wall-project { font-size: clamp(1.8rem, 4vw, 2.6rem); }
|
||||
.wall-time { font-size: clamp(1.7rem, 3.6vw, 2.6rem); }
|
||||
}
|
||||
.wall-live { color: #34d399; font-weight: 700; letter-spacing: 0.08em; }
|
||||
.wall-empty {
|
||||
min-height: 100vh; display: flex; flex-direction: column;
|
||||
@@ -1164,3 +1186,100 @@ footer {
|
||||
.health-table th, .health-table td { text-align: left; padding: 6px 10px; border-bottom: 1px solid var(--border, #22304a); }
|
||||
.health-slowest td { color: var(--warn, #e8a33d); font-weight: 600; }
|
||||
.zombie-actions { display: flex; gap: 6px; flex-shrink: 0; }
|
||||
|
||||
/* ── Header unifié (0.15.0, #16) ── */
|
||||
.header-actions { display: flex; gap: 6px; align-items: center; margin-left: auto; }
|
||||
.nav-burger {
|
||||
display: none; width: 40px; height: 40px; border-radius: 10px;
|
||||
background: var(--surface-2); border: 1px solid var(--border);
|
||||
color: var(--text); font-size: 1.05rem; cursor: pointer;
|
||||
align-items: center; justify-content: center;
|
||||
}
|
||||
.crumb a { color: inherit; text-decoration: none; }
|
||||
.crumb a:hover { text-decoration: underline; }
|
||||
@media (max-width: 900px) {
|
||||
.nav-burger { display: inline-flex; }
|
||||
.crumb { display: none; }
|
||||
}
|
||||
|
||||
/* ── Barre latérale (0.16.0, #16 refonte) ── */
|
||||
body.with-sidebar { padding-left: 238px; transition: padding-left 0.18s ease; }
|
||||
body.with-sidebar.sidebar-collapsed { padding-left: 68px; }
|
||||
.sidebar {
|
||||
position: fixed; left: 0; top: 0; bottom: 0; z-index: 200;
|
||||
width: 238px; box-sizing: border-box;
|
||||
display: flex; flex-direction: column; gap: 6px;
|
||||
padding: 16px 12px 12px; overflow-y: auto; overflow-x: hidden;
|
||||
background: rgba(13, 16, 24, 0.96); backdrop-filter: blur(14px);
|
||||
border-right: 1px solid var(--border);
|
||||
transition: width 0.18s ease, transform 0.22s ease;
|
||||
}
|
||||
body.sidebar-collapsed .sidebar { width: 68px; }
|
||||
.side-logo { display: flex; align-items: center; gap: 10px; padding: 4px 8px 12px; color: var(--text); font-size: 1.18rem; font-weight: 700; white-space: nowrap; }
|
||||
.side-logo:hover { text-decoration: none; }
|
||||
.side-search {
|
||||
display: flex; align-items: center; gap: 10px; width: 100%;
|
||||
padding: 9px 10px; margin-bottom: 8px; cursor: pointer;
|
||||
background: var(--surface-2); color: var(--text-2);
|
||||
border: 1px solid var(--border); border-radius: 10px; font-size: 0.9rem;
|
||||
white-space: nowrap; overflow: hidden;
|
||||
}
|
||||
.side-search:hover { color: var(--text); border-color: var(--accent, #58a6ff); }
|
||||
.side-search kbd {
|
||||
margin-left: auto; font-size: 0.72rem; background: var(--surface-3, #1d2942);
|
||||
border: 1px solid var(--border); border-radius: 5px; padding: 1px 6px;
|
||||
}
|
||||
.side-nav { display: flex; flex-direction: column; gap: 2px; }
|
||||
.side-group {
|
||||
margin: 14px 10px 5px; font-size: 0.7rem; font-weight: 700;
|
||||
letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-3);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.side-link {
|
||||
display: flex; align-items: center; gap: 11px;
|
||||
padding: 8px 10px; border-radius: 9px;
|
||||
color: var(--text-2); font-size: 0.95rem; font-weight: 500;
|
||||
white-space: nowrap; border-left: 3px solid transparent;
|
||||
}
|
||||
.side-link:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }
|
||||
.side-link.active {
|
||||
color: #fff; border-left-color: #a78bfa;
|
||||
background: linear-gradient(90deg, rgba(124,92,252,.32), rgba(91,138,247,.14));
|
||||
}
|
||||
.side-ico { font-size: 1.05rem; width: 22px; text-align: center; flex-shrink: 0; }
|
||||
.side-foot {
|
||||
margin-top: auto; padding-top: 12px;
|
||||
display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
||||
}
|
||||
.side-collapse {
|
||||
display: flex; align-items: center; gap: 8px; cursor: pointer;
|
||||
background: transparent; border: 1px solid var(--border); border-radius: 9px;
|
||||
color: var(--text-2); padding: 6px 10px; font-size: 0.85rem; white-space: nowrap;
|
||||
}
|
||||
.side-collapse:hover { color: var(--text); }
|
||||
.side-collapse-ico { display: inline-block; transition: transform 0.18s ease; }
|
||||
body.sidebar-collapsed .side-collapse-ico { transform: rotate(180deg); }
|
||||
.side-version { color: var(--text-3); font-size: 0.8rem; }
|
||||
body.sidebar-collapsed .side-label,
|
||||
body.sidebar-collapsed .side-group { display: none; }
|
||||
body.sidebar-collapsed .side-link,
|
||||
body.sidebar-collapsed .side-search { justify-content: center; padding-left: 0; padding-right: 0; }
|
||||
body.sidebar-collapsed .side-logo { justify-content: center; padding-left: 0; padding-right: 0; }
|
||||
body.sidebar-collapsed .side-foot { justify-content: center; }
|
||||
.side-backdrop {
|
||||
position: fixed; inset: 0; z-index: 190;
|
||||
background: rgba(4, 6, 10, 0.55);
|
||||
}
|
||||
.topbar { min-height: 52px; padding: 8px 18px; justify-content: flex-start; gap: 14px; }
|
||||
.topbar-search { display: none; }
|
||||
@media (max-width: 900px) {
|
||||
body.with-sidebar, body.with-sidebar.sidebar-collapsed { padding-left: 0; }
|
||||
.sidebar { width: 264px; transform: translateX(-105%); box-shadow: none; }
|
||||
body.sidebar-open .sidebar { transform: none; box-shadow: 18px 0 44px rgba(0,0,0,.5); }
|
||||
body.sidebar-collapsed .side-label,
|
||||
body.sidebar-collapsed .side-group { display: initial; }
|
||||
body.sidebar-collapsed .side-link,
|
||||
body.sidebar-collapsed .side-search { justify-content: flex-start; padding-left: 10px; padding-right: 10px; }
|
||||
.side-foot { display: none; }
|
||||
.topbar-search { display: inline-flex; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Kanbante — sidebar behaviour (issue #16, 0.16.0 layout).
|
||||
* Desktop: collapse to an icon rail (persisted in localStorage).
|
||||
* Mobile: the same sidebar becomes an off-canvas drawer, toggled
|
||||
* by the topbar burger, closed by backdrop / Escape / navigation.
|
||||
*/
|
||||
(function () {
|
||||
var burger = document.getElementById('nav-burger');
|
||||
var backdrop = document.getElementById('side-backdrop');
|
||||
var collapseBtn = document.getElementById('side-collapse');
|
||||
|
||||
function closeDrawer() {
|
||||
document.body.classList.remove('sidebar-open');
|
||||
if (backdrop) backdrop.hidden = true;
|
||||
if (burger) {
|
||||
burger.setAttribute('aria-expanded', 'false');
|
||||
burger.textContent = '☰';
|
||||
}
|
||||
}
|
||||
|
||||
if (burger) {
|
||||
burger.addEventListener('click', function () {
|
||||
var open = document.body.classList.toggle('sidebar-open');
|
||||
burger.setAttribute('aria-expanded', open ? 'true' : 'false');
|
||||
burger.textContent = open ? '✕' : '☰';
|
||||
if (backdrop) backdrop.hidden = !open;
|
||||
});
|
||||
}
|
||||
if (backdrop) backdrop.addEventListener('click', closeDrawer);
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Escape') closeDrawer();
|
||||
});
|
||||
window.addEventListener('resize', function () {
|
||||
if (window.innerWidth > 900) closeDrawer();
|
||||
});
|
||||
|
||||
if (collapseBtn) {
|
||||
collapseBtn.addEventListener('click', function () {
|
||||
var collapsed = document.body.classList.toggle('sidebar-collapsed');
|
||||
try {
|
||||
localStorage.setItem('kanbante-sidebar', collapsed ? 'collapsed' : 'open');
|
||||
} catch (e) { /* private mode: state lasts for the page only */ }
|
||||
});
|
||||
}
|
||||
})();
|
||||
@@ -0,0 +1,76 @@
|
||||
"""Palier 12 (0.15.0 → refonte 0.16.0): layout barre latérale (#16)
|
||||
et wallboard sans défilement parasite."""
|
||||
|
||||
import app.main as main
|
||||
|
||||
|
||||
def test_sidebar_on_app_pages(client, fake_gitea):
|
||||
for path in ("/", "/focus", "/today", "/briefing", "/forecast",
|
||||
"/rules", "/health", "/standup", "/capture"):
|
||||
page = client.get(path)
|
||||
assert page.status_code == 200, path
|
||||
assert page.text.count('id="sidebar"') == 1, path
|
||||
assert page.text.count('id="nav-burger"') == 1, path
|
||||
assert "with-sidebar" in page.text, path
|
||||
assert "/static/js/nav.js" in page.text, path
|
||||
# Les quatre groupes de la barre latérale.
|
||||
for group in ("Principal", "Rituels", "Pilotage", "Outils"):
|
||||
assert group in page.text, (path, group)
|
||||
assert 'href="/health"' in page.text
|
||||
assert 'id="side-collapse"' in page.text
|
||||
|
||||
|
||||
def test_sidebar_active_states(client, fake_gitea):
|
||||
home = client.get("/")
|
||||
assert 'href="/" class="side-link active"' in home.text
|
||||
forecast = client.get("/forecast")
|
||||
assert 'href="/forecast" class="side-link active"' in forecast.text
|
||||
today = client.get("/today")
|
||||
assert 'href="/today" class="side-link active"' in today.text
|
||||
|
||||
|
||||
def test_board_topbar_crumb_and_gitea_link(client, fake_gitea):
|
||||
board = client.get("/board/Projets/demo")
|
||||
assert board.status_code == 200
|
||||
assert 'href="/board/Projets/demo"' in board.text # fil d'Ariane topbar
|
||||
assert "Gitea ↗" in board.text
|
||||
assert 'id="sidebar"' in board.text
|
||||
|
||||
|
||||
def test_wall_has_no_sidebar(client, fake_gitea):
|
||||
wall = client.get("/wall")
|
||||
assert wall.status_code == 200
|
||||
assert 'id="sidebar"' not in wall.text # plein écran TV assumé
|
||||
assert "wall-foot" in wall.text
|
||||
|
||||
|
||||
def test_nav_js_contract(client, fake_gitea):
|
||||
js = client.get("/static/js/nav.js")
|
||||
assert js.status_code == 200
|
||||
assert "sidebar-collapsed" in js.text
|
||||
assert "localStorage" in js.text
|
||||
assert "side-backdrop" in js.text
|
||||
assert "Escape" in js.text
|
||||
|
||||
|
||||
def test_wall_more_cards_line(client, fake_gitea, monkeypatch):
|
||||
async def fake_collect(only_repo):
|
||||
return [{
|
||||
"full_name": "Projets/demo",
|
||||
"counts": {c: 0 for c in
|
||||
("backlog", "todo", "in_progress", "qa", "review", "done")},
|
||||
"open_total": 12,
|
||||
"cards": [
|
||||
{"number": i, "title": f"Carte {i}", "column": "in_progress",
|
||||
"column_label": "🚧 En cours", "age_days": 2, "aging": False}
|
||||
for i in range(1, 10)
|
||||
],
|
||||
"cards_total": 12,
|
||||
"closed_today": 1,
|
||||
}]
|
||||
|
||||
monkeypatch.setattr(main, "_collect_wall_data", fake_collect)
|
||||
page = client.get("/wall")
|
||||
assert page.status_code == 200
|
||||
assert "+3 autres cartes en vol" in page.text
|
||||
assert "📋 Ouvertes" in page.text
|
||||
Reference in New Issue
Block a user