feat: sidebar user menu Notion-style + settings page + tag colors + preview images/PDF
- Sidebar header: user menu dropdown (Settings, Switch workspace, Log out) avec avatar + nom + email, chevron animé, fond opaque #1E1E1E - Nouvelle page /settings avec gestion globale des tags: créer tag avec couleur, changer couleur, supprimer, liste avec compteurs - API tag management: POST/PUT/DELETE /api/settings/tags, GET /api/settings/tags/all - Preview panel: affichage images (img tag), PDF (lien viewer), détection format - workspace-chevron CSS: rotation 180° quand menu ouvert
This commit is contained in:
@@ -1080,6 +1080,80 @@ async def select_workspace(request: Request, ws_id: int):
|
||||
return response
|
||||
|
||||
|
||||
# ═══════════ Settings Page ═══════════
|
||||
|
||||
@router.get("/settings", response_class=HTMLResponse)
|
||||
async def app_settings_page(request: Request):
|
||||
"""Settings & configuration page."""
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
env = Environment(loader=FileSystemLoader("app/templates"))
|
||||
sidebar = _sidebar_data(request, [])
|
||||
user = SessionManager.decode_session(request.cookies.get("flowdeck_session", ""))
|
||||
if not user:
|
||||
return RedirectResponse("/auth/login?provider=local", status_code=302)
|
||||
ctx = {**sidebar, "user": user}
|
||||
template = env.get_template("settings.html")
|
||||
return template.render(**ctx)
|
||||
|
||||
|
||||
# ═══════════ Tag Management API (global) ═══════════
|
||||
|
||||
@router.post("/api/settings/tags")
|
||||
async def create_tag_global(request: Request):
|
||||
"""Create a tag with optional color."""
|
||||
import json
|
||||
body = await request.json()
|
||||
tag_name = body.get("name", "").strip().lower()
|
||||
color = body.get("color", "#787774")
|
||||
if not tag_name:
|
||||
return {"error": "Tag name required"}, 400
|
||||
with get_conn() as conn:
|
||||
tag = conn.execute("SELECT id FROM tags WHERE name = ?", (tag_name,)).fetchone()
|
||||
if tag:
|
||||
conn.execute("UPDATE tags SET color = ? WHERE id = ?", (color, tag["id"]))
|
||||
conn.commit()
|
||||
return {"tag": {"id": tag["id"], "name": tag_name, "color": color}}
|
||||
cursor = conn.execute("INSERT INTO tags (name, color) VALUES (?, ?)", (tag_name, color))
|
||||
conn.commit()
|
||||
return {"tag": {"id": cursor.lastrowid, "name": tag_name, "color": color}}
|
||||
|
||||
|
||||
@router.put("/api/settings/tags/{tag_id:int}")
|
||||
async def update_tag_global(tag_id: int, request: Request):
|
||||
"""Update a tag (name or color)."""
|
||||
import json
|
||||
body = await request.json()
|
||||
with get_conn() as conn:
|
||||
if "name" in body:
|
||||
conn.execute("UPDATE tags SET name = ? WHERE id = ?", (body["name"].strip().lower(), tag_id))
|
||||
if "color" in body:
|
||||
conn.execute("UPDATE tags SET color = ? WHERE id = ?", (body["color"], tag_id))
|
||||
conn.commit()
|
||||
return {"status": "ok"}
|
||||
|
||||
|
||||
@router.delete("/api/settings/tags/{tag_id:int}")
|
||||
async def delete_tag_global(tag_id: int):
|
||||
"""Delete a tag and its page associations."""
|
||||
with get_conn() as conn:
|
||||
conn.execute("DELETE FROM page_tags WHERE tag_id = ?", (tag_id,))
|
||||
conn.execute("DELETE FROM tags WHERE id = ?", (tag_id,))
|
||||
conn.commit()
|
||||
return {"status": "ok"}
|
||||
|
||||
|
||||
@router.get("/api/settings/tags/all")
|
||||
async def list_all_tags_global():
|
||||
"""List all tags with counts."""
|
||||
with get_conn() as conn:
|
||||
rows = conn.execute(
|
||||
"SELECT t.id, t.name, t.color, COUNT(pt.page_id) as count "
|
||||
"FROM tags t LEFT JOIN page_tags pt ON pt.tag_id = t.id "
|
||||
"GROUP BY t.id ORDER BY t.name"
|
||||
).fetchall()
|
||||
return {"tags": [dict(r) for r in rows]}
|
||||
|
||||
|
||||
# ═══════════ Tags API ═══════════
|
||||
|
||||
@router.get("/api/local-workspace/tags")
|
||||
|
||||
Reference in New Issue
Block a user