diff --git a/app/main.py b/app/main.py index b41a5b2..b951a44 100644 --- a/app/main.py +++ b/app/main.py @@ -13,7 +13,7 @@ from app.config import settings from app.db import init_db from app.middleware.csrf import CSRFMiddleware from app.middleware.security import ContentSecurityPolicyMiddleware, RateLimitMiddleware -from app.routers import dashboard, board, notes, api, auth, webhooks, collections, my_tasks, workspace, public_api, admin, sharing +from app.routers import dashboard, board, notes, api, auth, webhooks, collections, my_tasks, workspace, library, public_api, admin, sharing from app.routers.gitea import router as gitea_router from app.services.gitea_client import gitea from app.services.webhook_outbound import init_webhook_tables @@ -66,6 +66,7 @@ app.include_router(library.router) app.include_router(admin.router) app.include_router(gitea_router) app.include_router(public_api.router) +app.include_router(sharing.router) app.mount("/static", StaticFiles(directory="static"), name="static") diff --git a/app/routers/board.py b/app/routers/board.py index 3ce6e3c..d423836 100644 --- a/app/routers/board.py +++ b/app/routers/board.py @@ -303,13 +303,37 @@ def _sidebar_data(request: Request, owner: str = "", repo: str = "") -> dict: "children": [], }) + # Auth method & OAuth badge data + auth_method = "local" + gitea_linked = False + github_linked = False + if user and user.get("id"): + try: + with get_conn() as conn: + am_row = conn.execute("SELECT auth_method FROM users WHERE id=?", (user["id"],)).fetchone() + if am_row and am_row["auth_method"]: + auth_method = am_row["auth_method"] + tokens = conn.execute( + "SELECT provider FROM user_oauth_tokens WHERE user_id=?", (user["id"],) + ).fetchall() + for t in tokens: + if t["provider"] == "gitea": + gitea_linked = True + elif t["provider"] == "github": + github_linked = True + except Exception: + pass + return {"workspace_name": ws_name, "workspace_initial": ws_name[0].upper() if ws_name else "B", "active_ws_name": active_ws_name, "workspace_pages": _load_workspace_pages(ws_cookie), "current_page": repo or "Dashboard", "last_edited": "now", "recent_pages": recent, "private_pages": private_items, "favorite_pages": favorites, "shared_pages": shared_pages, - "user": user, "workspace_key": ws_key} + "user": user, "workspace_key": ws_key, + "auth_method": auth_method, + "gitea_linked": gitea_linked, + "github_linked": github_linked} def _extract_ai_keywords(owner: str, repo: str, labels: list[dict], body: str = ""): diff --git a/app/routers/dashboard.py b/app/routers/dashboard.py index 82d91c3..15c0bcd 100644 --- a/app/routers/dashboard.py +++ b/app/routers/dashboard.py @@ -157,75 +157,14 @@ async def trash_page(request: Request, owner: str = Query(default=""), repo: str @router.get("/library", response_class=HTMLResponse) async def library_page(request: Request, owner: str = Query(default=""), repo: str = Query(default="")): - """Library page avec onglets Recents, Favorites, Shared, Private — scopé au workspace. - - La sidebar garde exactement la même arborescence que dans le workspace. - Seul le contenu principal change pour afficher les pages triées par catégorie. + """Library page — tabbed view (recents, favorites, shared, published, private, workspace). + + Sidebar data is kept intact. Tab content is loaded client-side via /api/library/* endpoints. """ from jinja2 import Environment, FileSystemLoader from app.routers.board import _sidebar_data as board_sidebar env = Environment(loader=FileSystemLoader("app/templates")) - sidebar = board_sidebar(request, owner, repo) # Sidebar intacte, inchangée - - ws_key = f"{owner}/{repo}" if owner and repo else "" - with get_conn() as conn: - base_query = ( - "SELECT id, title, workspace, parent_section, updated_at FROM pages " - "WHERE parent_section != 'Trash'" - ) - params = [] - if ws_key: - base_query += " AND workspace=?" - params.append(ws_key) - base_query += " ORDER BY updated_at DESC" - rows = conn.execute(base_query, params).fetchall() - - def to_page(r): - r = dict(r) - return { - "id": f"page/{r['id']}", - "name": r["title"] or "Untitled", - "icon": "📄", - "url": f"/pages/{r['id']}", - "created_by": "You", - "source": r.get("workspace") or "🔒 Private", - "last_edited": r.get("updated_at", "now"), - "last_visited": r.get("updated_at", "now"), - "section": r.get("parent_section", "Private"), - } - - all_pages = [to_page(r) for r in rows] - - # Load favorites from DB for Library - user = SessionManager.decode_session(request.cookies.get("flowdeck_session", "")) - uid = user["id"] if user and user.get("id") else 1 - with get_conn() as conn: - fav_rows = conn.execute( - "SELECT p.id, p.title, p.workspace, p.updated_at FROM favorites f " - "JOIN pages p ON p.id = f.page_id " - "WHERE f.user_id=? ORDER BY f.position", (uid,) - ).fetchall() - lib_favs = [] - for r in fav_rows: - r = dict(r) - lib_favs.append({ - "id": f"page/{r['id']}", - "name": r["title"] or "Untitled", - "icon": "📄", - "url": f"/pages/{r['id']}", - "created_by": "You", - "source": r.get("workspace") or "🔒 Private", - "last_edited": r.get("updated_at", "now"), - "last_visited": r.get("updated_at", "now"), - }) - - # Catégoriser par parent_section — sidebar data intacte - sidebar["lib_recent"] = all_pages - sidebar["lib_favorites"] = lib_favs - sidebar["lib_shared"] = [] - sidebar["lib_private"] = [p for p in all_pages if p.get("section") == "Private"] - sidebar["lib_has_favorites"] = len(lib_favs) > 0 - sidebar["lib_has_shared"] = False + sidebar = board_sidebar(request, owner, repo) template = env.get_template("library.html") return template.render(**sidebar) diff --git a/app/templates/base.html b/app/templates/base.html index dae9de2..c2d25de 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -71,6 +71,11 @@
{% if not avatar_url %}{{ user.full_name[:1].upper() if user and user.get('full_name') else workspaceInitial }}{% endif %}
{{ user.full_name or user.login or workspaceName }} + {% if auth_method != 'local' %} + + {% if auth_method == 'gitea' %}🦎 Gitea{% elif auth_method == 'github' %}🐙 GitHub{% else %}🔑 {{ auth_method|title }}{% endif %} + + {% endif %}
▾ + → Library + {% if auth_method != 'local' or '/' in workspace_key %} + {% endif %} diff --git a/app/templates/library.html b/app/templates/library.html index 54a8fc9..3d4b224 100644 --- a/app/templates/library.html +++ b/app/templates/library.html @@ -11,122 +11,341 @@ - -
+
-
- - {% if lib_has_favorites %} - - {% endif %} - {% if lib_has_shared %} - - {% endif %} - -
- - -
-
- - - - -
- - -
- -
- 📄 Page name - 👤 Created by - ↗ Source - 🕒 Last edited time - 🕒 Last visited time + +
+ + + + + +
- {% macro page_row(page) %} - - - {{ page.icon }} - {{ page.name }} - - 👤 {{ page.created_by or 'You' }} - {{ page.source or '🔒 Private' }} - {{ page.last_edited }} - {{ page.last_visited or page.last_edited }} - - {% endmacro %} - - -
- {% for page in lib_recent %}{{ page_row(page) }}{% endfor %} - {% if not lib_recent %} -
-
🕒
-

No recent pages

+ +
+
+ + + +
- {% endif %} +
+ +
- -
- {% for page in lib_favorites %}{{ page_row(page) }}{% endfor %} - {% if not lib_favorites %} -
-
⭐
-

No favorites

-

Right-click a page in the sidebar and select "Add to Favorites".

-
- {% endif %} + +
+
+

Loading…

- -
- {% for page in lib_shared %}{{ page_row(page) }}{% endfor %} - {% if not lib_shared %} -
-
👥
-

No shared pages

-

Shared pages from your team will appear here.

-
- {% endif %} + +
+
- -
- {% for page in lib_private %}{{ page_row(page) }}{% endfor %} - {% if not lib_private %} -
-
🔒
-

No private pages

-

Create a new page with the + button in the sidebar.

-
- {% endif %} + +
+
+

+

-
+
{% endblock %} {% block scripts %} + + {% endblock %} diff --git a/tests/test_app.py b/tests/test_app.py index c388aeb..49635e7 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -209,7 +209,8 @@ def test_db_tables_exist(client): "collections", "collection_pages", "collection_views", "collection_properties", "workspaces", "workspace_members", "comments", "page_history", "favorites", - "database_templates", "page_templates"} + "database_templates", "page_templates", + "page_shares", "recents"} assert required <= names