""" Geschützter App-Bereich: Dashboard-Lab Layout (kein Produktiv-Dashboard). /api/app/dashboard-layout — nur mit Session + aktivem Profil (X-Profile-Id). """ from typing import Any, Optional from fastapi import APIRouter, Depends, Header, HTTPException from psycopg2.extras import Json from auth import require_auth from dashboard_layout_schema import DashboardLayoutPayload, coalesce_effective_layout, default_layout_dict from db import get_cursor, get_db from routers.profiles import get_pid from widget_catalog import catalog_response router = APIRouter(prefix="/api/app", tags=["app-dashboard-lab"]) @router.get("/widgets/catalog") def get_widgets_catalog(session: dict = Depends(require_auth)) -> dict[str, Any]: """Metadaten aller registrierbaren Dashboard-Widgets (IDs, Titel).""" _ = session return catalog_response() @router.get("/dashboard-layout") def get_dashboard_layout( x_profile_id: Optional[str] = Header(default=None), session: dict = Depends(require_auth), ) -> dict[str, Any]: _ = session pid = get_pid(x_profile_id) with get_db() as conn: cur = get_cursor(conn) cur.execute( "SELECT dashboard_layout FROM profiles WHERE id = %s", (pid,), ) row = cur.fetchone() raw = row["dashboard_layout"] if row else None custom, effective = coalesce_effective_layout(raw) return { "custom": custom, "layout": effective, "default_layout": default_layout_dict(), } @router.put("/dashboard-layout") def put_dashboard_layout( body: dict[str, Any], x_profile_id: Optional[str] = Header(default=None), session: dict = Depends(require_auth), ) -> dict[str, Any]: _ = session pid = get_pid(x_profile_id) try: payload = DashboardLayoutPayload.model_validate(body) except Exception as e: raise HTTPException(422, str(e)) from e stored = payload.to_stored_dict() with get_db() as conn: cur = get_cursor(conn) cur.execute( "UPDATE profiles SET dashboard_layout = %s WHERE id = %s", (Json(stored), pid), ) if cur.rowcount == 0: raise HTTPException(404, "Profil nicht gefunden") return {"ok": True, "layout": stored} @router.post("/dashboard-layout/reset") def reset_dashboard_layout( x_profile_id: Optional[str] = Header(default=None), session: dict = Depends(require_auth), ) -> dict[str, Any]: _ = session pid = get_pid(x_profile_id) with get_db() as conn: cur = get_cursor(conn) cur.execute( "UPDATE profiles SET dashboard_layout = NULL WHERE id = %s", (pid,), ) if cur.rowcount == 0: raise HTTPException(404, "Profil nicht gefunden") return {"ok": True, "layout": default_layout_dict()}