Skip to main content

max / makenotwork

2.4 KB · 84 lines History Blame Raw
1 //! Multi-step creation wizards for projects and items.
2 //!
3 //! Each wizard is a full page with a sidebar step indicator + content area.
4 //! Steps are HTMX partials swapped into `#wizard-step`. Each step form POSTs
5 //! to save, and the server responds with the next step partial. The DB record
6 //! IS the wizard state; step 1 creates it, subsequent steps update it.
7
8 pub mod item;
9 pub mod project;
10
11 use axum::routing::get;
12 use crate::{
13 csrf::{post_csrf, with_csrf, CsrfRouter},
14 AppState,
15 };
16
17 /// Step navigation helpers shared by both wizards.
18 pub fn next_step<'a>(steps: &'a [&str], current: &str) -> Option<&'a str> {
19 steps
20 .iter()
21 .position(|&s| s == current)
22 .and_then(|i| steps.get(i + 1))
23 .copied()
24 }
25
26 fn step_index(steps: &[&str], current: &str) -> Option<usize> {
27 steps.iter().position(|&s| s == current)
28 }
29
30 use crate::templates::StepNavItem;
31
32 /// Build the list of step display info for the sidebar nav.
33 pub fn build_step_nav(
34 steps: &[&'static str],
35 labels: &[&'static str],
36 current: &str,
37 ) -> Vec<StepNavItem> {
38 let current_idx = step_index(steps, current).unwrap_or(0);
39 steps
40 .iter()
41 .zip(labels.iter())
42 .enumerate()
43 .map(|(i, (&name, &label))| StepNavItem {
44 name,
45 label,
46 state: if i < current_idx {
47 "completed"
48 } else if i == current_idx {
49 "active"
50 } else {
51 "pending"
52 },
53 })
54 .collect()
55 }
56
57 /// Register all wizard routes.
58 pub fn wizard_routes() -> CsrfRouter<AppState> {
59 CsrfRouter::new()
60 // Project wizard
61 .route_get("/dashboard/new-project", get(project::wizard_page))
62 .route(
63 "/dashboard/new-project/step/basics",
64 post_csrf(project::step_basics_create),
65 )
66 .route(
67 "/dashboard/new-project/{slug}/step/{step}",
68 with_csrf(get(project::step_load).post(project::step_save)),
69 )
70 // Item wizard
71 .route_get(
72 "/dashboard/project/{slug}/new-item",
73 get(item::wizard_page),
74 )
75 .route(
76 "/dashboard/project/{slug}/new-item/step/type",
77 post_csrf(item::step_type_create),
78 )
79 .route(
80 "/dashboard/project/{slug}/new-item/{id}/step/{step}",
81 with_csrf(get(item::step_load).post(item::step_save)),
82 )
83 }
84