"""Admin gallery curation endpoints. Allows super admins to toggle is_gallery_featured and update gallery_sort_order on Tree (flows) and ScriptTemplate (scripts) records. """ from typing import Annotated from uuid import UUID from fastapi import APIRouter, Depends, HTTPException, status from pydantic import BaseModel from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.api.deps import require_admin from app.core.database import get_db from app.models.script_template import ScriptTemplate from app.models.tree import Tree from app.models.user import User router = APIRouter(prefix="/admin/gallery", tags=["admin-gallery"]) # --------------------------------------------------------------------------- # Request schemas # --------------------------------------------------------------------------- class FeatureToggle(BaseModel): is_gallery_featured: bool class SortOrderUpdate(BaseModel): gallery_sort_order: int # --------------------------------------------------------------------------- # Response helpers # --------------------------------------------------------------------------- def _flow_summary(tree: Tree) -> dict: return { "id": str(tree.id), "name": tree.name, "tree_type": tree.tree_type, "is_gallery_featured": tree.is_gallery_featured, "gallery_sort_order": tree.gallery_sort_order, "visibility": tree.visibility, } def _script_summary(script: ScriptTemplate) -> dict: return { "id": str(script.id), "name": script.name, "is_gallery_featured": script.is_gallery_featured, "gallery_sort_order": script.gallery_sort_order, "is_active": script.is_active, } # --------------------------------------------------------------------------- # Endpoints # --------------------------------------------------------------------------- @router.get("/featured") async def list_featured( db: Annotated[AsyncSession, Depends(get_db)], current_user: Annotated[User, Depends(require_admin)], ): """List all featured flows and scripts (super admin only).""" flows_result = await db.execute( select(Tree) .where(Tree.is_gallery_featured == True) # noqa: E712 .order_by(Tree.gallery_sort_order.asc(), Tree.name.asc()) ) flows = flows_result.scalars().all() scripts_result = await db.execute( select(ScriptTemplate) .where(ScriptTemplate.is_gallery_featured == True) # noqa: E712 .order_by(ScriptTemplate.gallery_sort_order.asc(), ScriptTemplate.name.asc()) ) scripts = scripts_result.scalars().all() return { "flows": [_flow_summary(f) for f in flows], "scripts": [_script_summary(s) for s in scripts], } @router.get("/items") async def list_all_items( db: Annotated[AsyncSession, Depends(get_db)], current_user: Annotated[User, Depends(require_admin)], ): """List ALL flows and scripts with their gallery status (super admin only).""" flows_result = await db.execute( select(Tree) .where(Tree.visibility == "public") .order_by(Tree.gallery_sort_order.asc(), Tree.name.asc()) ) flows = flows_result.scalars().all() scripts_result = await db.execute( select(ScriptTemplate) .order_by(ScriptTemplate.gallery_sort_order.asc(), ScriptTemplate.name.asc()) ) scripts = scripts_result.scalars().all() return { "flows": [_flow_summary(f) for f in flows], "scripts": [_script_summary(s) for s in scripts], } @router.patch("/flows/{flow_id}/feature") async def toggle_flow_featured( flow_id: UUID, body: FeatureToggle, db: Annotated[AsyncSession, Depends(get_db)], current_user: Annotated[User, Depends(require_admin)], ): """Toggle is_gallery_featured on a flow (super admin only).""" result = await db.execute(select(Tree).where(Tree.id == flow_id)) tree = result.scalar_one_or_none() if not tree: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Flow not found") tree.is_gallery_featured = body.is_gallery_featured await db.commit() await db.refresh(tree) return _flow_summary(tree) @router.patch("/flows/{flow_id}/sort-order") async def update_flow_sort_order( flow_id: UUID, body: SortOrderUpdate, db: Annotated[AsyncSession, Depends(get_db)], current_user: Annotated[User, Depends(require_admin)], ): """Update gallery_sort_order on a flow (super admin only).""" result = await db.execute(select(Tree).where(Tree.id == flow_id)) tree = result.scalar_one_or_none() if not tree: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Flow not found") tree.gallery_sort_order = body.gallery_sort_order await db.commit() await db.refresh(tree) return _flow_summary(tree) @router.patch("/scripts/{script_id}/feature") async def toggle_script_featured( script_id: UUID, body: FeatureToggle, db: Annotated[AsyncSession, Depends(get_db)], current_user: Annotated[User, Depends(require_admin)], ): """Toggle is_gallery_featured on a script (super admin only).""" result = await db.execute(select(ScriptTemplate).where(ScriptTemplate.id == script_id)) script = result.scalar_one_or_none() if not script: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Script not found") script.is_gallery_featured = body.is_gallery_featured await db.commit() await db.refresh(script) return _script_summary(script) @router.patch("/scripts/{script_id}/sort-order") async def update_script_sort_order( script_id: UUID, body: SortOrderUpdate, db: Annotated[AsyncSession, Depends(get_db)], current_user: Annotated[User, Depends(require_admin)], ): """Update gallery_sort_order on a script (super admin only).""" result = await db.execute(select(ScriptTemplate).where(ScriptTemplate.id == script_id)) script = result.scalar_one_or_none() if not script: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Script not found") script.gallery_sort_order = body.gallery_sort_order await db.commit() await db.refresh(script) return _script_summary(script)