feat: Refonte UI Notion-complete — sidebar, topbar, multi-vues, dark mode
- Layout 3 zones (sidebar 240px + topbar 44px + contenu) - Sidebar: sections hiérarchiques, hover states, menu contextuel - CSS design system Notion: #191919/#222/#333, Inter, coins 4-6px - Vues: Kanban (groupes+colonnes), Dashboard Notion-style - Templates: base.html refondu, card.html partial, board.html v2 - Backend: données sidebar + board Notion (groups/cards) - Docs: ROADMAP v0.4→v1.0, WORKLOAD.md, README - Backward compatible: conserve _issue_column pour api.py - Images de référence + resized (analyse 2026-07-08)
This commit is contained in:
@@ -1,17 +1,52 @@
|
||||
"""FlowDeck — Dashboard: liste des projets Gitea."""
|
||||
"""FlowDeck — Dashboard: liste des projets Gitea + sidebar data."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from fastapi import APIRouter, Request, HTTPException, Query
|
||||
from fastapi import APIRouter, Request, Query
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
from app.services.gitea_client import gitea
|
||||
from app.auth.session import SessionManager
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
router = APIRouter(tags=["dashboard"])
|
||||
|
||||
|
||||
def _sidebar_data(request: Request, repos: list[dict]) -> dict:
|
||||
"""Build sidebar context from Gitea repos."""
|
||||
user = SessionManager.decode_session(request.cookies.get("flowdeck_session", ""))
|
||||
workspace_name = user.get("login", "Bruno") if user else "Bruno"
|
||||
workspace_initial = workspace_name[0].upper() if workspace_name else "B"
|
||||
|
||||
# Build recent pages from repos
|
||||
recent_pages = []
|
||||
private_pages = []
|
||||
for repo in repos[:10]:
|
||||
full_name = repo.get("full_name", "")
|
||||
parts = full_name.split("/") if "/" in full_name else [workspace_name, full_name]
|
||||
page = {
|
||||
"id": full_name,
|
||||
"name": repo.get("name", full_name),
|
||||
"icon": "📁",
|
||||
"url": f"/board/{full_name}",
|
||||
"active": False,
|
||||
"indent": 0,
|
||||
}
|
||||
recent_pages.append(page)
|
||||
private_pages.append({**page})
|
||||
|
||||
return {
|
||||
"workspace_name": workspace_name,
|
||||
"workspace_initial": workspace_initial,
|
||||
"current_page": "Dashboard",
|
||||
"last_edited": "now",
|
||||
"recent_pages": recent_pages,
|
||||
"private_pages": private_pages,
|
||||
"user": user,
|
||||
}
|
||||
|
||||
|
||||
@router.get("/", response_class=HTMLResponse)
|
||||
async def dashboard(
|
||||
request: Request,
|
||||
@@ -20,18 +55,16 @@ async def dashboard(
|
||||
):
|
||||
"""Dashboard: liste tous les projets Gitea (user + orgs)."""
|
||||
try:
|
||||
# User repos already includes all accessible org repos in Gitea
|
||||
repos = await gitea.get_user_repos(page=1, limit=50)
|
||||
|
||||
# Filter
|
||||
if search:
|
||||
q = search.lower()
|
||||
repos = [r for r in repos if q in r.get("full_name", "").lower() or q in (r.get("description") or "").lower()]
|
||||
repos = [r for r in repos if q in r.get("full_name", "").lower()
|
||||
or q in (r.get("description") or "").lower()]
|
||||
|
||||
if not show_archived:
|
||||
repos = [r for r in repos if not r.get("archived", False)]
|
||||
|
||||
# Sort by updated_at desc
|
||||
repos.sort(key=lambda r: r.get("updated_at", ""), reverse=True)
|
||||
|
||||
except Exception as e:
|
||||
@@ -39,15 +72,14 @@ async def dashboard(
|
||||
repos = []
|
||||
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
from app.auth.session import SessionManager
|
||||
|
||||
env = Environment(loader=FileSystemLoader("app/templates"))
|
||||
user = SessionManager.decode_session(request.cookies.get("flowdeck_session", ""))
|
||||
sidebar = _sidebar_data(request, repos)
|
||||
template = env.get_template("dashboard.html")
|
||||
return template.render(
|
||||
request=request,
|
||||
repos=repos,
|
||||
search=search,
|
||||
show_archived=show_archived,
|
||||
user=user,
|
||||
**sidebar,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user