//! Browse page — paginated quote list with author and tag filter controls. use crate::api; use crate::components::error::ErrorDisplay; use crate::components::pagination::Pagination; use crate::components::quote_card::QuoteCard; use quotesdb::Quote; use wasm_bindgen_futures::spawn_local; use web_sys::HtmlInputElement; use yew::prelude::*; /// Browse page component. /// /// Displays a paginated list of quotes. Supports filtering by author name /// and tag. Fetches from the API whenever page, author, or tag state changes. #[function_component(BrowsePage)] pub fn browse_page() -> Html { let page = use_state(|| 1u32); let total_pages = use_state(|| 1u32); let author_filter = use_state(String::new); let tag_filter = use_state(String::new); let quotes: UseStateHandle> = use_state(Vec::new); let error: UseStateHandle> = use_state(|| None); let loading = use_state(|| true); // Fetch quotes whenever page, author, or tag changes { let page = page.clone(); let total_pages = total_pages.clone(); let author_filter = author_filter.clone(); let tag_filter = tag_filter.clone(); let quotes = quotes.clone(); let error = error.clone(); let loading = loading.clone(); let page_val = *page; let author_val = (*author_filter).clone(); let tag_val = (*tag_filter).clone(); use_effect_with((page_val, author_val.clone(), tag_val.clone()), move |_| { loading.set(true); error.set(None); let author = if author_val.is_empty() { None } else { Some(author_val.clone()) }; let tag = if tag_val.is_empty() { None } else { Some(tag_val.clone()) }; spawn_local(async move { match api::list_quotes(page_val, author.as_deref(), tag.as_deref()).await { Ok(resp) => { quotes.set(resp.quotes); total_pages.set(resp.total_pages.max(1)); loading.set(false); } Err(e) => { error.set(Some(e.to_string())); loading.set(false); } } }); }); } let on_page = { let page = page.clone(); Callback::from(move |p: u32| page.set(p)) }; let on_author_input = { let author_filter = author_filter.clone(); let page = page.clone(); Callback::from(move |e: InputEvent| { let input: HtmlInputElement = e.target_unchecked_into(); author_filter.set(input.value()); page.set(1); }) }; let on_tag_input = { let tag_filter = tag_filter.clone(); let page = page.clone(); Callback::from(move |e: InputEvent| { let input: HtmlInputElement = e.target_unchecked_into(); tag_filter.set(input.value()); page.set(1); }) }; html! {

{ "Browse Quotes" }

if *loading {

{ "Loading..." }

} else if let Some(err) = (*error).clone() { } else if quotes.is_empty() {

{ "No quotes found." }

} else {
{ for (*quotes).iter().map(|q| html! { }) }
}
} }