-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: added snapshot_list and single view UI
- Loading branch information
Showing
7 changed files
with
366 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
use leptos::*; | ||
|
||
use chrono::{DateTime, TimeZone, Utc}; | ||
use leptos_router::A; | ||
use serde::{Deserialize, Serialize}; | ||
use serde_json::{json, Map, Value}; | ||
|
||
use crate::components::skeleton::Skeleton; | ||
use crate::components::stat::Stat; | ||
use crate::components::table::types::TablePaginationProps; | ||
use crate::components::table::{types::Column, Table}; | ||
use crate::types::{ListFilters, Snapshot}; | ||
|
||
use crate::api::get_snapshots; | ||
|
||
#[derive(Serialize, Deserialize, Clone, Debug)] | ||
struct CombinedResource { | ||
snapshots: Vec<Snapshot>, | ||
} | ||
|
||
#[component] | ||
pub fn snapshot_list() -> impl IntoView { | ||
let tenant_rs = use_context::<ReadSignal<String>>().unwrap(); | ||
|
||
// Signals for filters | ||
let (filters, set_filters) = create_signal(ListFilters { | ||
status: None, | ||
from_date: Utc.timestamp_opt(0, 0).single(), | ||
to_date: Utc.timestamp_opt(4130561031, 0).single(), | ||
page: Some(1), | ||
count: Some(10), // Limit of 10 items per page | ||
}); | ||
|
||
let table_columns = | ||
create_memo(move |_| snapshot_table_columns(tenant_rs.get_untracked())); | ||
|
||
let combined_resource: Resource<String, CombinedResource> = create_blocking_resource( | ||
move || tenant_rs.get(), | ||
|current_tenant| async move { | ||
match get_snapshots(current_tenant.to_string()).await { | ||
Ok(response) => CombinedResource { | ||
snapshots: response.data, | ||
}, | ||
Err(_) => CombinedResource { snapshots: vec![] }, | ||
} | ||
}, | ||
); | ||
|
||
let handle_next_click = Callback::new(move |total_pages: i64| { | ||
set_filters.update(|f| { | ||
f.page = match f.page { | ||
Some(p) if p < total_pages => Some(p + 1), | ||
Some(p) => Some(p), | ||
None => Some(1), | ||
} | ||
}); | ||
}); | ||
|
||
let handle_prev_click = Callback::new(move |_| { | ||
set_filters.update(|f| { | ||
f.page = match f.page { | ||
Some(p) if p > 1 => Some(p - 1), | ||
Some(_) => Some(1), | ||
None => Some(1), | ||
} | ||
}); | ||
}); | ||
|
||
view! { | ||
<div class="p-8"> | ||
<Suspense fallback=move || view! { <Skeleton/> }> | ||
<div class="pb-4"> | ||
{move || { | ||
let snapshot_res = combined_resource.get(); | ||
let total_items = match snapshot_res { | ||
Some(snapshot_resp) => snapshot_resp.snapshots.len().to_string(), | ||
_ => "0".to_string(), | ||
}; | ||
view! { | ||
<Stat heading="Snapshots" icon="ri-camera-lens-fill" number=total_items/> | ||
} | ||
}} | ||
</div> | ||
<div class="card rounded-xl w-full bg-base-100 shadow"> | ||
<div class="card-body"> | ||
<div class="flex justify-between"> | ||
<h2 class="card-title">Snapshots</h2> | ||
</div> | ||
<div> | ||
{move || { | ||
let value = combined_resource.get(); | ||
let filters = filters.get(); | ||
match value { | ||
Some(v) => { | ||
// Pagination calculations | ||
let page = filters.page.unwrap_or(1); | ||
let count = filters.count.unwrap_or(10); | ||
let total_items = v.snapshots.len(); | ||
let total_pages = (total_items as f64 / count as f64).ceil() as i64; | ||
|
||
// Get the items for the current page | ||
let start = ((page - 1) * count as i64) as usize; | ||
let end = std::cmp::min(start + count as usize, total_items); | ||
|
||
let data = v | ||
.snapshots[start..end] | ||
.iter() | ||
.map(|snapshot| { | ||
let mut snapshot_map = json!(snapshot) | ||
.as_object() | ||
.unwrap() | ||
.to_owned(); | ||
|
||
if let Some(id_value) = snapshot_map.get("id") { | ||
let id_string = match id_value { | ||
Value::Number(num) => num.to_string(), | ||
Value::String(s) => s.clone(), | ||
_ => "".to_string(), | ||
}; | ||
snapshot_map.insert("id".to_string(), Value::String(id_string)); | ||
} | ||
|
||
// Format the created_at field | ||
if let Some(created_at_str) = snapshot_map.get("created_at").and_then(|v| v.as_str()) { | ||
if let Ok(created_at_dt) = DateTime::parse_from_rfc3339(created_at_str) { | ||
snapshot_map.insert( | ||
"created_at".to_string(), | ||
json!(created_at_dt.format("%v").to_string()), | ||
); | ||
} | ||
} | ||
|
||
snapshot_map | ||
}) | ||
.collect::<Vec<Map<String, Value>>>() | ||
.to_owned(); | ||
|
||
let pagination_props = TablePaginationProps { | ||
enabled: true, | ||
count, | ||
current_page: page, | ||
total_pages, | ||
on_next: handle_next_click, | ||
on_prev: handle_prev_click, | ||
}; | ||
|
||
view! { | ||
<Table | ||
cell_class="".to_string() | ||
rows=data | ||
key_column="id".to_string() | ||
columns=table_columns.get() | ||
pagination=pagination_props | ||
/> | ||
} | ||
} | ||
None => view! { <div>Loading....</div> }.into_view(), | ||
} | ||
}} | ||
</div> | ||
</div> | ||
</div> | ||
</Suspense> | ||
</div> | ||
} | ||
} | ||
|
||
pub fn snapshot_table_columns(tenant: String) -> Vec<Column> { | ||
vec![ | ||
Column::new( | ||
"id".to_string(), | ||
None, | ||
|value: &str, _row: &Map<String, Value>| { | ||
let id = value.to_string(); | ||
view! { | ||
<div>{id}</div> | ||
} | ||
.into_view() | ||
}, | ||
), | ||
Column::new( | ||
"config".to_string(), | ||
None, | ||
move |_value: &str, row: &Map<String, Value>| { | ||
let id = row.get("id").and_then(|v| v.as_str()).unwrap_or_default(); | ||
let href = format!("/admin/{}/snapshots/{}", tenant, id); | ||
view! { | ||
<div> | ||
<A href=href class="btn-link"> | ||
"View Config" | ||
</A> | ||
</div> | ||
} | ||
.into_view() | ||
}, | ||
), | ||
Column::default("config_hash".to_string()), | ||
Column::default("created_at".to_string()), | ||
Column::new( | ||
"tags".to_string(), | ||
None, | ||
|_value: &str, row: &Map<String, Value>| { | ||
let tags = row.get("tags").and_then(|v| v.as_array()); | ||
match tags { | ||
Some(arr) => { | ||
let tags_str = arr | ||
.iter() | ||
.map(|v| v.as_str().unwrap_or("")) | ||
.collect::<Vec<&str>>() | ||
.join(", "); | ||
view! { <span>{tags_str}</span> } | ||
} | ||
None => view! { <span>"-"</span> }, | ||
} | ||
.into_view() | ||
}, | ||
), | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
use crate::api::get_config_by_version; | ||
use leptos::*; | ||
use leptos_router::use_params_map; | ||
|
||
#[component] | ||
pub fn SnapshotConfig() -> impl IntoView { | ||
let params = use_params_map(); | ||
let tenant = params.with(|p| p.get("tenant").cloned().unwrap_or_default()); | ||
let version = params.with(|p| p.get("version").cloned().unwrap_or_default()); | ||
|
||
view! { | ||
<div class="p-8"> | ||
{ | ||
let config_resource = create_blocking_resource( | ||
move || (tenant.clone(), version.clone()), | ||
|(tenant, version)| async move { | ||
get_config_by_version(tenant, version).await | ||
}, | ||
); | ||
|
||
view! { | ||
<Suspense fallback=move || view! { <p>"Loading..."</p> }.into_view()> | ||
{move || { | ||
match config_resource.get() { | ||
Some(Ok(config)) => { | ||
// Display the config JSON | ||
let config_json = serde_json::to_string_pretty(&config).unwrap_or_default(); | ||
view! { | ||
<div> | ||
<andypf-json-viewer | ||
indent="4" | ||
expanded="true" | ||
theme="default-light" | ||
show-data-types="false" | ||
show-toolbar="true" | ||
expand-icon-type="arrow" | ||
expanded="1" | ||
show-copy="true" | ||
show-size="false" | ||
data=config_json | ||
></andypf-json-viewer> | ||
|
||
</div> | ||
} | ||
.into_view() | ||
} | ||
Some(Err(_)) => view! { <div> <pre>"Error loading config."</pre></div> }.into_view(), | ||
None => view! { <div> <pre>"Loading..."</pre></div> }.into_view(), | ||
} | ||
}} | ||
</Suspense> | ||
} | ||
.into_view() | ||
} | ||
</div> | ||
} | ||
} |
Oops, something went wrong.