diff --git a/app/templates/activity.html b/app/templates/activity.html index dbb7db7..09337b9 100644 --- a/app/templates/activity.html +++ b/app/templates/activity.html @@ -20,6 +20,7 @@ 📰 Activité 📊 Briefing 🔍 Recherche + 🖥️ Wallboard diff --git a/app/templates/briefing.html b/app/templates/briefing.html index 5e2e6b5..f290eb3 100644 --- a/app/templates/briefing.html +++ b/app/templates/briefing.html @@ -20,6 +20,7 @@ 👤 Mes issues 📰 Activité 🔍 Recherche + 🖥️ Wallboard diff --git a/app/templates/focus.html b/app/templates/focus.html index 481c503..8a60743 100644 --- a/app/templates/focus.html +++ b/app/templates/focus.html @@ -20,6 +20,7 @@ 📰 Activité 📊 Briefing 🔍 Recherche + 🖥️ Wallboard {{ groups|map(attribute='cards')|map('length')|sum }} cartes en cours diff --git a/app/templates/my.html b/app/templates/my.html index 460ad5d..32697a5 100644 --- a/app/templates/my.html +++ b/app/templates/my.html @@ -19,6 +19,7 @@ 👤 Mes issues 📰 Activité 🔍 Recherche + 🖥️ Wallboard diff --git a/app/templates/search.html b/app/templates/search.html index ae08b1a..36f6208 100644 --- a/app/templates/search.html +++ b/app/templates/search.html @@ -19,6 +19,7 @@ 👤 Mes issues 📰 Activité 🔍 Recherche + 🖥️ Wallboard diff --git a/app/templates/wall.html b/app/templates/wall.html new file mode 100644 index 0000000..57ce551 --- /dev/null +++ b/app/templates/wall.html @@ -0,0 +1,70 @@ + + + + + +Kanbante — Wallboard + + + +{% if slides %} +
+ {% for slide in slides %} +
+
+
+
📂 Projet
+

{{ slide.full_name }}

+
+
+
--:--
+
+ {% if not single and slides|length > 1 %} +
{{ loop.index }} / {{ slides|length }}
+ {% endif %} +
+
+ +
+ {% for col in columns %} +
+ {{ slide.counts[col] }} + {{ column_labels[col] }} +
+ {% endfor %} +
+ + {% if slide.cards %} + + {% else %} +

🌿 Rien en vol pour le moment — tout est au Backlog ou terminé.

+ {% endif %} + + +
+ {% endfor %} +
+{% else %} +
+

🖥️ Wallboard

+

Aucun projet actif à afficher{% if single %} pour ce dépôt{% endif %}.

+

Astuce : /wall?repo=Proprietaire/depot fige un seul projet.

+
+{% endif %} + + + diff --git a/static/css/style.css b/static/css/style.css index a55d792..4b4a1de 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -893,3 +893,77 @@ footer { scroll-behavior: auto !important; } } + +/* ── Wallboard (0.6.0) : affichage plein écran, lisible à 3 mètres ── */ +.wall-body { + background: #0b1220; + color: #e5ecf5; + margin: 0; + font-family: inherit; +} +.wall-stage { position: relative; min-height: 100vh; } +.wall-slide { + display: none; + min-height: 100vh; + box-sizing: border-box; + padding: 4vh 4vw; + flex-direction: column; + gap: 3vh; +} +.wall-slide.active { display: flex; animation: wall-in 0.45s ease; } +@keyframes wall-in { from { opacity: 0; } to { opacity: 1; } } +.wall-head { + display: flex; justify-content: space-between; align-items: flex-start; gap: 24px; +} +.wall-kicker { font-size: 1rem; letter-spacing: 0.22em; text-transform: uppercase; color: #8fa3bf; } +.wall-project { font-size: clamp(2.2rem, 5.2vw, 4.2rem); margin: 0.4rem 0 0; line-height: 1.05; } +.wall-clock { text-align: right; } +.wall-time { font-size: clamp(2rem, 4.5vw, 3.6rem); font-weight: 800; line-height: 1; } +.wall-date { color: #8fa3bf; font-size: 1.15rem; margin-top: 0.35rem; text-transform: capitalize; } +.wall-rotation { color: #5b6b84; margin-top: 0.4rem; font-size: 1rem; } +.wall-counts { + display: grid; grid-template-columns: repeat(6, 1fr); gap: 14px; +} +.wall-count { + border: 1.5px solid var(--col); + border-top: 6px solid var(--col); + border-radius: 14px; + padding: 14px 10px 12px; + text-align: center; + background: rgba(255, 255, 255, 0.03); +} +.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-card { + display: flex; align-items: center; gap: 18px; + background: rgba(255, 255, 255, 0.045); + border-radius: 14px; + padding: 16px 20px; + font-size: clamp(1.15rem, 2.1vw, 1.7rem); +} +.wall-card-col { + border: 1.5px solid var(--col); color: var(--col); + border-radius: 999px; padding: 4px 14px; + font-size: 0.62em; font-weight: 700; white-space: nowrap; +} +.wall-card-title { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.wall-card-age { color: #fbbf24; font-weight: 700; white-space: nowrap; } +.wall-card-age.hot { color: #f87171; } +.wall-idle { font-size: 1.5rem; color: #8fa3bf; } +.wall-foot { + margin-top: auto; + display: flex; gap: 36px; flex-wrap: wrap; + font-size: clamp(1rem, 1.7vw, 1.35rem); color: #aebdd2; +} +.wall-live { color: #34d399; font-weight: 700; letter-spacing: 0.08em; } +.wall-empty { + min-height: 100vh; display: flex; flex-direction: column; + align-items: center; justify-content: center; gap: 10px; text-align: center; + color: #aebdd2; padding: 24px; +} +.wall-empty h1 { color: #e5ecf5; font-size: 2.6rem; margin: 0; } +.wall-empty code { background: rgba(255,255,255,0.08); padding: 2px 8px; border-radius: 6px; } +@media (max-width: 900px) { + .wall-counts { grid-template-columns: repeat(3, 1fr); } +} diff --git a/static/js/wall.js b/static/js/wall.js new file mode 100644 index 0000000..f0b9be7 --- /dev/null +++ b/static/js/wall.js @@ -0,0 +1,42 @@ +/** + * Kanbante — Wallboard: slide rotation, clock, live refresh. + * + * The wall is a display, not a workspace: no interaction to protect, + * so a realtime event simply reloads the page (plus a slow safety + * reload in case the stream drops silently). + */ +(function () { + var slides = Array.from(document.querySelectorAll('.wall-slide')); + var ROTATE_MS = 30000; + var current = 0; + + if (slides.length > 1) { + setInterval(function () { + slides[current].classList.remove('active'); + current = (current + 1) % slides.length; + slides[current].classList.add('active'); + }, ROTATE_MS); + } + + function tickClock() { + var now = new Date(); + var time = now.toLocaleTimeString('fr-CA', { hour: '2-digit', minute: '2-digit' }); + var date = now.toLocaleDateString('fr-CA', { + weekday: 'long', day: 'numeric', month: 'long', + }); + document.querySelectorAll('[data-clock]').forEach(function (el) { el.textContent = time; }); + document.querySelectorAll('[data-date]').forEach(function (el) { el.textContent = date; }); + } + tickClock(); + setInterval(tickClock, 15000); + + // Live: any board activity refreshes the wall. + try { + var source = new EventSource('/api/stream'); + source.onmessage = function () { location.reload(); }; + } catch (err) { + console.warn('Wallboard live stream unavailable:', err); + } + // Safety net. + setTimeout(function () { location.reload(); }, 5 * 60 * 1000); +})();