//! Scene detail/edit page component.
use leptos::prelude::*;
use leptos_router::hooks::use_params_map;
#[cfg(feature = "hydrate")]
use leptos::task::spawn_local;
use uuid::Uuid;
use crate::components::{Card, DetailGrid, DetailItem, PageHeader};
#[cfg(feature = "hydrate")]
use crate::utils::fetch_image_dimensions_client;
/// Scene detail from API.
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct SceneDetail {
pub id: Uuid,
pub realm_id: Uuid,
pub name: String,
pub slug: String,
pub description: Option,
pub background_image_path: Option,
pub background_color: Option,
pub bounds_wkt: String,
pub dimension_mode: String,
pub sort_order: i32,
pub is_entry_point: bool,
pub is_hidden: bool,
pub created_at: String,
pub updated_at: String,
}
/// Parse width and height from WKT bounds string.
/// Example: "POLYGON((0 0, 800 0, 800 600, 0 600, 0 0))" -> (800, 600)
/// Handles both "0 0, 800 0" (with space) and "0 0,800 0" (without space) formats.
fn parse_bounds_wkt(wkt: &str) -> (i32, i32) {
// Extract coordinates from POLYGON((x1 y1, x2 y2, x3 y3, x4 y4, x5 y5))
// The second point has (width, 0) and third point has (width, height)
if let Some(start) = wkt.find("((") {
if let Some(end) = wkt.find("))") {
let coords_str = &wkt[start + 2..end];
let points: Vec<&str> = coords_str.split(',').map(|s| s.trim()).collect();
if points.len() >= 3 {
// Second point: "width 0"
let second: Vec<&str> = points[1].split_whitespace().collect();
// Third point: "width height"
let third: Vec<&str> = points[2].split_whitespace().collect();
if !second.is_empty() && third.len() >= 2 {
let width = second[0].parse().unwrap_or(800);
let height = third[1].parse().unwrap_or(600);
return (width, height);
}
}
}
}
(800, 600)
}
/// Scene detail page component.
#[component]
pub fn SceneDetailPage() -> impl IntoView {
let params = use_params_map();
let realm_slug = move || params.get().get("slug").unwrap_or_default();
let scene_id = move || params.get().get("scene_id").unwrap_or_default();
let initial_realm_slug = params.get_untracked().get("slug").unwrap_or_default();
let (message, set_message) = signal(Option::<(String, bool)>::None);
let scene = LocalResource::new(move || {
let id = scene_id();
async move {
if id.is_empty() {
return None;
}
#[cfg(feature = "hydrate")]
{
use gloo_net::http::Request;
let resp = Request::get(&format!("/api/admin/scenes/{}", id)).send().await;
match resp {
Ok(r) if r.ok() => r.json::().await.ok(),
_ => None,
}
}
#[cfg(not(feature = "hydrate"))]
{
let _ = id;
None::
}
}
});
let slug_for_back = initial_realm_slug.clone();
view! {
"Back to Scenes"
"Loading scene..."
}>
{move || {
let realm_slug_val = realm_slug();
scene.get().map(|maybe_scene| {
match maybe_scene {
Some(s) => view! {
}.into_any(),
None => view! {
"Scene not found or you don't have permission to view."
}.into_any()
}
})
}}
}
}
#[component]
#[allow(unused_variables)]
fn SceneDetailView(
scene: SceneDetail,
realm_slug: String,
message: ReadSignal