"""Dashboard widget configuration endpoints.""" import logging import uuid from typing import Any from fastapi import APIRouter, Depends from pydantic import BaseModel from sqlalchemy.ext.asyncio import AsyncSession from app.database import get_db from app.domains.admin.dashboard_service import ( get_user_dashboard_config, upsert_user_dashboard_config, upsert_tenant_default, ) from app.utils.auth import get_current_user, require_global_admin from app.models.user import User logger = logging.getLogger(__name__) router = APIRouter(prefix="/dashboard", tags=["dashboard"]) # --------------------------------------------------------------------------- # Schemas # --------------------------------------------------------------------------- class WidgetPosition(BaseModel): col: int row: int w: int h: int class WidgetConfig(BaseModel): widget_type: str position: WidgetPosition config: dict[str, Any] | None = None class DashboardConfigPayload(BaseModel): widgets: list[WidgetConfig] class DashboardConfigResponse(BaseModel): widgets: list[WidgetConfig] # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def _to_response(widgets: list[dict]) -> DashboardConfigResponse: parsed = [] for w in widgets: pos = w.get("position", {}) parsed.append( WidgetConfig( widget_type=w.get("widget_type", ""), position=WidgetPosition( col=pos.get("col", 0), row=pos.get("row", 0), w=pos.get("w", 1), h=pos.get("h", 1), ), config=w.get("config"), ) ) return DashboardConfigResponse(widgets=parsed) # --------------------------------------------------------------------------- # Endpoints # --------------------------------------------------------------------------- @router.get("/config", response_model=DashboardConfigResponse) async def get_config( current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ) -> DashboardConfigResponse: """Load the current user's dashboard widget config (with fallback cascade).""" widgets = await get_user_dashboard_config( db=db, user_id=current_user.id, tenant_id=current_user.tenant_id, role=current_user.role.value, ) return _to_response(widgets) @router.put("/config", response_model=DashboardConfigResponse) async def update_config( payload: DashboardConfigPayload, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ) -> DashboardConfigResponse: """Save or update the current user's dashboard widget config.""" widgets_raw = [w.model_dump() for w in payload.widgets] config = await upsert_user_dashboard_config( db=db, user_id=current_user.id, tenant_id=current_user.tenant_id, widgets=widgets_raw, ) return _to_response(list(config.widgets)) @router.get("/tenant-default", response_model=DashboardConfigResponse) async def get_tenant_default( current_user: User = Depends(require_global_admin), db: AsyncSession = Depends(get_db), ) -> DashboardConfigResponse: """Load the tenant-default dashboard widget config (admin only).""" from sqlalchemy import select from app.domains.admin.models import DashboardConfig if current_user.tenant_id is None: return DashboardConfigResponse(widgets=[]) result = await db.execute( select(DashboardConfig).where( DashboardConfig.tenant_id == current_user.tenant_id, DashboardConfig.is_tenant_default.is_(True), ) ) config = result.scalar_one_or_none() if config is None: return DashboardConfigResponse(widgets=[]) return _to_response(list(config.widgets)) @router.put("/tenant-default", response_model=DashboardConfigResponse) async def update_tenant_default( payload: DashboardConfigPayload, current_user: User = Depends(require_global_admin), db: AsyncSession = Depends(get_db), ) -> DashboardConfigResponse: """Set the tenant-default widget config (admin only).""" if current_user.tenant_id is None: from fastapi import HTTPException, status raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Admin user has no tenant_id assigned.", ) widgets_raw = [w.model_dump() for w in payload.widgets] config = await upsert_tenant_default( db=db, tenant_id=current_user.tenant_id, widgets=widgets_raw, ) return _to_response(list(config.widgets))