ui: deep linking and url history

This commit is contained in:
Jeremy Wall 2024-02-23 18:18:33 -05:00
parent 8aa41eb68c
commit 0af85229c2
2 changed files with 30 additions and 6 deletions

View File

@ -90,6 +90,7 @@ async fn main() -> anyhow::Result<()> {
.nest("/api", routes::mk_api_routes(config.clone())) .nest("/api", routes::mk_api_routes(config.clone()))
// HTMX ui component endpoints // HTMX ui component endpoints
.nest("/ui", routes::mk_ui_routes(config.clone())) .nest("/ui", routes::mk_ui_routes(config.clone()))
.route("/dash/:dash_idx", get(routes::dashboard_direct))
.route("/", get(routes::index).with_state(State(config.clone()))) .route("/", get(routes::index).with_state(State(config.clone())))
.layer(TraceLayer::new_for_http()) .layer(TraceLayer::new_for_http())
.with_state(State(config.clone())); .with_state(State(config.clone()));

View File

@ -96,6 +96,10 @@ pub async fn graph_ui(
pub async fn dash_ui(State(config): State<Config>, Path(dash_idx): Path<usize>) -> Markup { pub async fn dash_ui(State(config): State<Config>, Path(dash_idx): Path<usize>) -> Markup {
// TODO(zaphar): Should do better http error reporting here. // TODO(zaphar): Should do better http error reporting here.
dash_elements(config, dash_idx)
}
fn dash_elements(config: State<Arc<Vec<Dashboard>>>, dash_idx: usize) -> maud::PreEscaped<String> {
let dash = config.get(dash_idx).expect("No such dashboard"); let dash = config.get(dash_idx).expect("No such dashboard");
let graph_iter = dash let graph_iter = dash
.graphs .graphs
@ -123,7 +127,7 @@ pub fn mk_ui_routes(config: Arc<Vec<Dashboard>>) -> Router<Config> {
) )
} }
pub async fn index(State(config): State<Config>) -> Markup { async fn index_html(config: Config, dash_idx: Option<usize>) -> Markup {
html! { html! {
html { html {
head { head {
@ -132,15 +136,26 @@ pub async fn index(State(config): State<Config>) -> Markup {
body { body {
script src="/js/plotly.js" { } script src="/js/plotly.js" { }
script src="/js/htmx.js" { } script src="/js/htmx.js" { }
script src="/js/lib.js" { } script defer src="/js/lib.js" { }
link rel="stylesheet" href="/static/site.css" { } link rel="stylesheet" href="/static/site.css" { }
(app(State(config.clone())).await) (app(State(config.clone()), dash_idx).await)
} }
} }
} }
} }
pub async fn app(State(config): State<Config>) -> Markup { pub async fn index(State(config): State<Config>) -> Markup {
index_html(config, None).await
}
pub async fn dashboard_direct(
State(config): State<Config>,
Path(dash_idx): Path<usize>,
) -> Markup {
index_html(config, Some(dash_idx)).await
}
fn render_index(config: State<Arc<Vec<Dashboard>>>, dash_idx: Option<usize>) -> Markup {
let titles = config let titles = config
.iter() .iter()
.map(|d| d.title.clone()) .map(|d| d.title.clone())
@ -152,15 +167,23 @@ pub async fn app(State(config): State<Config>) -> Markup {
// Header menu // Header menu
ul { ul {
@for title in &titles { @for title in &titles {
li hx-get=(format!("/ui/dash/{}", title.0)) hx-target="#dashboard" { (title.1) } li hx-push-url=(format!("/dash/{}", title.0)) hx-get=(format!("/ui/dash/{}", title.0)) hx-target="#dashboard" { (title.1) }
} }
} }
} }
div class="flex-item-grow" id="dashboard" { } div class="flex-item-grow" id="dashboard" {
@if let Some(dash_idx) = dash_idx {
(dash_elements(config, dash_idx))
}
}
} }
} }
} }
pub async fn app(State(config): State<Config>, dash_idx: Option<usize>) -> Markup {
render_index(config, dash_idx)
}
pub fn javascript_response(content: &str) -> Response<String> { pub fn javascript_response(content: &str) -> Response<String> {
Response::builder() Response::builder()
.header("Content-Type", "text/javascript") .header("Content-Type", "text/javascript")