diff --git a/crates/frontend/src/api.rs b/crates/frontend/src/api.rs index 0b7398ca..fbe7461e 100644 --- a/crates/frontend/src/api.rs +++ b/crates/frontend/src/api.rs @@ -3,7 +3,7 @@ use leptos::ServerFnError; use crate::{ types::{ Config, DefaultConfig, Dimension, ExperimentResponse, ExperimentsResponse, - FetchTypeTemplateResponse, FunctionResponse, ListFilters, + FetchTypeTemplateResponse, FunctionResponse, ListFilters, SnapshotResponse, }, utils::{ construct_request_headers, get_host, parse_json_response, request, @@ -51,6 +51,24 @@ pub async fn fetch_default_config( Ok(response) } +pub async fn fetch_snapshots(tenant: String) -> Result { + let client = reqwest::Client::new(); + let host = use_host_server(); + + let url = format!("{host}/config/versions"); + let response: SnapshotResponse = client + .get(url) + .header("x-tenant", tenant) + .send() + .await + .map_err(|e| ServerFnError::new(e.to_string()))? + .json() + .await + .map_err(|e| ServerFnError::new(e.to_string()))?; + + Ok(response) +} + pub async fn delete_context( tenant: String, context_id: String, @@ -160,11 +178,17 @@ pub async fn fetch_function( } // #[server(GetConfig, "/fxn", "GetJson")] -pub async fn fetch_config(tenant: String) -> Result { +pub async fn fetch_config( + tenant: String, + version: Option, +) -> Result { let client = reqwest::Client::new(); let host = use_host_server(); - let url = format!("{}/config", host); + let url = match version { + Some(version) => format!("{}/config?version={}", host, version), + None => format!("{}/config", host), + }; match client.get(url).header("x-tenant", tenant).send().await { Ok(response) => { let config: Config = response diff --git a/crates/frontend/src/app.rs b/crates/frontend/src/app.rs index f7c2fb87..d54b60d3 100755 --- a/crates/frontend/src/app.rs +++ b/crates/frontend/src/app.rs @@ -9,6 +9,8 @@ use crate::pages::experiment_list::ExperimentList; use crate::pages::function::{ function_create::CreateFunctionView, function_list::FunctionList, FunctionPage, }; +use crate::pages::snapshot::Snapshot; +use crate::pages::snapshot_list::SnapshotList; use crate::pages::{ context_override::ContextOverride, custom_types::TypesPage, default_config::DefaultConfig, experiment::ExperimentPage, home::Home, @@ -211,6 +213,30 @@ pub fn app(app_envs: Envs) -> impl IntoView { } /> + + + + } + } + /> + + + + + } + } + /> + // ( button_class = button_class + "hover:cursor-not-allowed"; } view! { - } } diff --git a/crates/frontend/src/components/condition_pills.rs b/crates/frontend/src/components/condition_pills.rs index 80d6d5dc..254f72bb 100644 --- a/crates/frontend/src/components/condition_pills.rs +++ b/crates/frontend/src/components/condition_pills.rs @@ -59,32 +59,39 @@ pub fn condition_expression( } else { ("condition-item-collapsed", "condition-value-collapsed") }; - - // Destructure the condition - let Condition { left_operand: dimension, operator, right_operand: value } = condition.get_value(); - - // Filter and convert values to strings for rendering - let filtered_vals: Vec = value.into_iter().filter_map(|v| - if v.is_object() && v.get("var").is_some() { - None - } else { - match v { - Value::String(s) => Some(s.to_string()), - Value::Number(n) => Some(n.to_string()), - Value::Bool(b) => Some(b.to_string()), - Value::Array(arr) => { - Some(arr.iter().map(|v| v.to_string()).collect::>().join(",")) - } - Value::Object(o) => { - serde_json::to_string_pretty(&o).ok() + let Condition { left_operand: dimension, operator, right_operand: value } = condition + .get_value(); + let filtered_vals: Vec = value + .into_iter() + .filter_map(|v| { + if v.is_object() && v.get("var").is_some() { + None + } else { + match v { + Value::String(s) => Some(s.to_string()), + Value::Number(n) => Some(n.to_string()), + Value::Bool(b) => Some(b.to_string()), + Value::Array(arr) => { + Some( + arr + .iter() + .map(|v| v.to_string()) + .collect::>() + .join(","), + ) + } + Value::Object(o) => serde_json::to_string_pretty(&o).ok(), + _ => None, + } } - _ => None, - } - } - ).collect(); - - // Render based on the operator type + }) + .collect(); view! { + // Destructure the condition + + // Filter and convert values to strings for rendering + + // Render based on the operator type
  • - { - match operator { - ConditionOperator::Between => { - if filtered_vals.len() == 2 { - view! { - <> - - {&filtered_vals[0]} - - - {"and"} - - - {&filtered_vals[1]} - - - }.into_view() - } else { - view! { "Invalid between values" }.into_view() + {match operator { + ConditionOperator::Between => { + if filtered_vals.len() == 2 { + view! { + <> + + {&filtered_vals[0]} + + + {"and"} + + + {&filtered_vals[1]} + + } - }, - _ => { - let rendered_value = filtered_vals.join(", "); + .into_view() + } else { view! { - - {rendered_value} + + "Invalid between values" - }.into_view() + } + .into_view() } } - } + _ => { + let rendered_value = filtered_vals.join(", "); + view! { {rendered_value} }.into_view() + } + }} +
  • } }} diff --git a/crates/frontend/src/components/context_card.rs b/crates/frontend/src/components/context_card.rs index 06162cec..123ae8b2 100644 --- a/crates/frontend/src/components/context_card.rs +++ b/crates/frontend/src/components/context_card.rs @@ -70,14 +70,16 @@ pub fn context_card( on:click=move |_| { handle_edit.call((context.get_value(), overrides.get_value())); } - > + > + + > + @@ -91,7 +93,8 @@ pub fn context_card( let context_id = context_id.get_value(); handle_delete.call(context_id); } - > + > + diff --git a/crates/frontend/src/components/dimension_form.rs b/crates/frontend/src/components/dimension_form.rs index bee6c59b..97ce0f4e 100644 --- a/crates/frontend/src/components/dimension_form.rs +++ b/crates/frontend/src/components/dimension_form.rs @@ -273,17 +273,18 @@ where
    - { move || { - let loading = req_inprogess_rs.get(); - view! { -
    { diff --git a/crates/frontend/src/components/experiment.rs b/crates/frontend/src/components/experiment.rs index f6ee6637..13c5cff8 100644 --- a/crates/frontend/src/components/experiment.rs +++ b/crates/frontend/src/components/experiment.rs @@ -162,12 +162,10 @@ where for token in contexts.clone() { let (dimension, values) = (token.left_operand, token.right_operand); let mut value_views = Vec::new(); - for value in values.iter() { if value.is_object() && value.get("var").is_some() { continue; } - let value_str = match value { Value::String(s) => s.clone(), Value::Number(n) => n.to_string(), @@ -175,23 +173,23 @@ where Value::Null => String::from("null"), _ => format!("{}", value), }; - - value_views.push( - view! { + value_views + .push( + view! {
    {&value_str.replace('"', "")}
    - }, - ); - + }, + ); } - view.push(view! { -
    -
    {dimension}
    - {value_views} -
    - }); - + view.push( + view! { +
    +
    {dimension}
    + {value_views} +
    + }, + ); } view }} diff --git a/crates/frontend/src/components/experiment_form.rs b/crates/frontend/src/components/experiment_form.rs index 66903595..b85f9973 100644 --- a/crates/frontend/src/components/experiment_form.rs +++ b/crates/frontend/src/components/experiment_form.rs @@ -152,7 +152,8 @@ where let context = f_context.get(); view! { + // dimensions will now be a Vec + dimensions=dimensions.get_value() context=context handle_change=handle_context_form_change is_standalone=false @@ -181,17 +182,18 @@ where }}
    - { move || { - let loading = req_inprogess_rs.get(); - view! { -
    } diff --git a/crates/frontend/src/components/experiment_ramp_form.rs b/crates/frontend/src/components/experiment_ramp_form.rs index d69f53ff..cc9702a6 100644 --- a/crates/frontend/src/components/experiment_ramp_form.rs +++ b/crates/frontend/src/components/experiment_ramp_form.rs @@ -42,7 +42,7 @@ where - { move || { + + {move || { let loading = req_inprogess_rs.get(); view! {