//! Spot management API handlers for admin UI. use axum::{ Json, extract::{Path, State}, }; use chattyness_db::{ models::{CreateSpotRequest, Spot, SpotSummary, UpdateSpotRequest}, queries::spots, }; use chattyness_error::AppError; use serde::Serialize; use sqlx::PgPool; use uuid::Uuid; use crate::auth::AdminConn; /// List all spots for a scene. pub async fn list_spots( State(pool): State, Path(scene_id): Path, ) -> Result>, AppError> { let spot_list = spots::list_spots_for_scene(&pool, scene_id).await?; Ok(Json(spot_list)) } /// Get a spot by ID. pub async fn get_spot( State(pool): State, Path(spot_id): Path, ) -> Result, AppError> { let spot = spots::get_spot_by_id(&pool, spot_id) .await? .ok_or_else(|| AppError::NotFound("Spot not found".to_string()))?; Ok(Json(spot)) } /// Create spot response. #[derive(Debug, Serialize)] pub struct CreateSpotResponse { pub id: Uuid, } /// Create a new spot in a scene. pub async fn create_spot( admin_conn: AdminConn, Path(scene_id): Path, Json(req): Json, ) -> Result, AppError> { let conn = admin_conn.0; let mut guard = conn.acquire().await; // Check if slug is available (if provided) if let Some(ref slug) = req.slug { let available = spots::is_spot_slug_available(&mut *guard, scene_id, slug).await?; if !available { return Err(AppError::Conflict(format!( "Spot slug '{}' is already taken in this scene", slug ))); } } let spot = spots::create_spot(&mut *guard, scene_id, &req).await?; Ok(Json(CreateSpotResponse { id: spot.id })) } /// Update a spot. pub async fn update_spot( admin_conn: AdminConn, Path(spot_id): Path, Json(req): Json, ) -> Result, AppError> { let conn = admin_conn.0; let mut guard = conn.acquire().await; // If updating slug, check availability if let Some(ref new_slug) = req.slug { let existing = spots::get_spot_by_id(&mut *guard, spot_id) .await? .ok_or_else(|| AppError::NotFound("Spot not found".to_string()))?; if Some(new_slug.clone()) != existing.slug { let available = spots::is_spot_slug_available(&mut *guard, existing.scene_id, new_slug).await?; if !available { return Err(AppError::Conflict(format!( "Spot slug '{}' is already taken in this scene", new_slug ))); } } } let spot = spots::update_spot(&mut *guard, spot_id, &req).await?; Ok(Json(spot)) } /// Delete a spot. pub async fn delete_spot( admin_conn: AdminConn, Path(spot_id): Path, ) -> Result, AppError> { let conn = admin_conn.0; let mut guard = conn.acquire().await; spots::delete_spot(&mut *guard, spot_id).await?; Ok(Json(())) }