use std::rc::Rc; use serde::Deserialize; use sycamore::prelude::*; use crate::{ api::types::paginated_response::{IntoTableData, PaginatedResponse}, components::{ base_table::{BaseTable, TableContentRx}, loading::Loading, }, }; #[derive(Clone)] pub struct PaginatedTableStateRx where M: 'static, C: Fn(Option, i64, i64) -> F, F: std::future::Future, ()>>, { pub route: C, pub filter: Option, } impl PaginatedTableStateRx where M: 'static, C: Fn(Option, i64, i64) -> F, F: std::future::Future, ()>>, { async fn get_data(&self, page: i64, size: i64) -> Result, ()> { (self.route)(self.filter.clone(), page, size).await } } #[component(PaginatedTable)] pub fn component(state: PaginatedTableStateRx) -> View where M: 'static + Clone, PaginatedResponse: IntoTableData, for<'de> M: Deserialize<'de>, C: Fn(Option, i64, i64) -> F + 'static, F: std::future::Future, ()>> + 'static, { let paginated_data: Signal>> = Signal::new(None); let table_prop: TableContentRx = TableContentRx { headers_view: Signal::new(vec![]), data_view: Signal::new(vec![vec![]]), }; let table_prop2 = table_prop.clone(); let page: Signal = Signal::new(0); let n_page: Signal = Signal::new(1); let n_rows: Signal = Signal::new(0); let page_up = cloned!((page, paginated_data, n_page) => move |_| { n_page.set((*paginated_data.get()).as_ref().map_or(0, |t| t.num_pages)); if *page.get() + 1 < *n_page.get() { page.set((*page.get()).min(*n_page.get() - 1) + 1) } }); let page_down = cloned!((page) => move |_| { if *page.get() > 0 { page.set((*page.get()-1).max(0)); } }); let page_size_string = Signal::new("20".to_string()); let page_size_string2 = page_size_string.clone(); let state_rc = Rc::new(state); create_effect( cloned!((page_size_string, paginated_data, page, n_page, n_rows, state_rc) => move || { let page = *page.get(); let page_size_s = page_size_string.get(); let page_size = page_size_s.parse().unwrap_or(20); if G::IS_BROWSER { perseus::spawn_local( cloned!((table_prop2, page, paginated_data, n_page, n_rows, state_rc) => async move { let res = state_rc.get_data(page, page_size).await.unwrap(); paginated_data.set(Some(res.clone())); n_rows.set(res.count); let table_content = res.into_table_data(); table_prop2.data_view.set(table_content.data_view); table_prop2.headers_view.set(table_content.headers_view); n_page.set((*paginated_data.get()).as_ref().map_or(0, |t| t.num_pages)); }), ); } }), ); view! { (cloned!((n_rows, page_size_string, page_down, page_up, page, n_page, page_size_string2) => view! { p (class="text-right") { (format!("{} transactions", n_rows.get())) } div (class="flex flex-row justify-between") { select (bind:value=page_size_string, class="p-2 justify-end text-slate-700 dark:text-slate-100 bg-slate-200 dark:bg-slate-800 rounded-md", id="size-select", ) { option (value="20") { "20" } option (value="10") { "10" } option (value="30", selected=page_size_string2.get().eq(&Rc::new("30".to_string()))) { "30" } option (value="40") { "40" } option (value="50") { "50" } } div (class="flex flex-row p-2 bg-slate-200 dark:bg-slate-800 rounded-md") { button (on:click=page_down,class="m-1 hover:font-bold") { "<<" } div (class="m-1 align-middle text-center") { (format!("{}/{}",*page.get() + 1, *n_page.get()) ) } button (on:click=page_up, class="m-1 hover:font-bold") { ">>" } } } })) (if paginated_data.get().is_some() { view! { BaseTable(table_prop.clone()) } } else { view! { div (class="flex flex-row justify-center") { Loading() } } }) } }