//! Step rendering dispatcher for the item wizard. use axum::response::{IntoResponse, Response}; use tower_sessions::Session; use crate::{ db::{self, ItemType, ProjectFeature}, error::{AppError, Result}, helpers::get_csrf_token, templates::*, AppState, }; use super::{build_step_nav, format_price_display, ITEM_LABELS, ITEM_STEPS}; pub(super) async fn render_step( state: &AppState, session: &Session, _user: &crate::auth::SessionUser, project: &db::DbProject, item: &db::DbItem, step: &str, ) -> Result { let nav = build_step_nav(ITEM_STEPS, ITEM_LABELS, step); let project_slug = project.slug.to_string(); let item_id = item.id.to_string(); let csrf_token = get_csrf_token(session).await; match step { "type" => { let type_cards = ProjectFeature::wizard_type_cards(&project.features); // If only 1 behavior group, skip forward to basics if type_cards.len() <= 1 { let nav = build_step_nav(ITEM_STEPS, ITEM_LABELS, "basics"); return Ok(WizardItemBasicsTemplate { nav, project_slug, item_id, title: item.title.clone(), description: item.description.clone().unwrap_or_default(), cover_image_url: item.cover_image_url.clone(), } .into_response()); } Ok(WizardItemTypeTemplate { nav, project_slug, item_id, item_type_cards: type_cards, selected_type: item.item_type.to_string(), } .into_response()) } "basics" => Ok(WizardItemBasicsTemplate { nav, project_slug, item_id, title: item.title.clone(), description: item.description.clone().unwrap_or_default(), cover_image_url: item.cover_image_url.clone(), } .into_response()), "content" => { let content_template = item.item_type.to_string(); // Load bundle data if this is a bundle item let (bundleable_items, selected_bundle_ids, unlisted_ids) = if item.item_type == ItemType::Bundle { let bundleable = db::bundles::get_bundleable_items(&state.db, project.id, Some(item.id)) .await?; let current = db::bundles::get_bundle_items(&state.db, item.id).await?; let selected: Vec = current.iter().map(|i| i.id.to_string()).collect(); let unlisted: Vec = current .iter() .chain(bundleable.iter()) .filter(|i| !i.listed) .map(|i| i.id.to_string()) .collect(); let bi: Vec = bundleable .iter() .map(|i| BundleableItem { id: i.id.to_string(), title: i.title.clone(), item_type: i.item_type.label().to_string(), }) .collect(); (bi, selected, unlisted) } else { (vec![], vec![], vec![]) }; Ok(WizardItemContentTemplate { nav, project_slug, project_id: project.id.to_string(), item_id, item_type: content_template, body: item.body.clone().unwrap_or_default(), bundleable_items, selected_bundle_ids, unlisted_ids, } .into_response()) } "sections" => { let db_sections = db::item_sections::list_by_item(&state.db, item.id).await?; let sections: Vec = db_sections.iter().map(crate::types::ItemSection::from).collect(); Ok(WizardItemSectionsTemplate { nav, project_slug, item_id, sections, } .into_response()) } "pricing" => { let pricing_model = if item.pwyw_enabled { "pwyw" } else if item.price_cents > 0 { "fixed" } else { "free" }; Ok(WizardItemPricingTemplate { nav, project_slug, item_id, pricing_model: pricing_model.to_string(), price_dollars: format!( "{}.{:02}", item.price_cents / 100, item.price_cents % 100 ), pwyw_suggested_dollars: format!( "{}.{:02}", item.price_cents / 100, item.price_cents % 100 ), pwyw_min_dollars: { let min = item.pwyw_min_cents.unwrap_or(0); format!("{}.{:02}", min / 100, min % 100) }, } .into_response()) } "preview" => { let tags = db::tags::get_tags_for_item(&state.db, item.id).await?; let tag_names: Vec = tags.iter().map(|t| t.tag_name.clone()).collect(); Ok(WizardItemPreviewTemplate { csrf_token, nav, project_slug, item_id, title: item.title.clone(), item_type: item.item_type.to_string(), description: item.description.clone().unwrap_or_default(), price_display: format_price_display(item.price_cents, item.pwyw_enabled, item.pwyw_min_cents), tag_names, has_content: item.body.is_some() || item.audio_s3_key.is_some() || item.video_s3_key.is_some() || (item.item_type == ItemType::Bundle && db::bundles::get_bundle_item_count(&state.db, item.id).await? > 0), is_public: item.is_public, } .into_response()) } _ => Err(AppError::NotFound), } }