use jl_types::{domain::{project_state::ProjectState, project_type::ProjectType, project_condition::ProjectCondition}, dto::{filters::Filter, project_card::ProjectCardDto}}; use log::info; use yew::prelude::*; use yew_utils::{components::drop_down::{DropDownProps, DropDown}, vdom::comp_with}; use jl_types::domain::option_wrapper::OptionWrapper; use crate::{components::{nav_bar::NavigationBar, project_card::ProjectCard}, api::backend::{get_all_cities, get_all_districts_in_city, get_all_projects_with_filters_paged}}; #[function_component(SearchPage)] pub fn search_page(props: &SearchPageProperties) -> Html { // let force_update_trigger = use_force_update(); let cities_handle = use_state(|| Vec::from([OptionWrapper::new(None)])); let districts_handle = use_state(|| Vec::from([OptionWrapper::new(None)])); let search_results_handle: UseStateHandle> = use_state(|| Vec::new()); let page_counter: UseStateHandle = use_state(|| 1); let finished_loading = use_state(|| false); let mut filters = Vec::new(); if props.project_state.eq(&ProjectState::Finished) { filters.push(Filter::Finished); } // All code to execute on first render and never again use_state(|| { let cities_handle = cities_handle.clone(); let search_results_handle = search_results_handle.clone(); let page_counter = page_counter.clone(); let finished_loading = finished_loading.clone(); wasm_bindgen_futures::spawn_local(async move { match get_all_cities().await { Ok(cities) => { let mut cities: Vec> = cities.into_iter().map(|location| OptionWrapper::new(Some(location))).collect(); cities.insert(0, OptionWrapper::new(None)); cities_handle.set(cities); }, Err(error) => info!("Error in loading cities: {error}") }; match get_all_projects_with_filters_paged(&(*page_counter), filters).await { Ok(projects) => { search_results_handle.set(projects); finished_loading.set(true); }, Err(error) => info!("Error in loading projects: {error}"), }; }); }); // Dropdown let project_type_filter: UseStateHandle> = use_state(|| OptionWrapper::new(None)); // Dropdown let project_condition_filter: UseStateHandle> = use_state(|| OptionWrapper::new(None)); // Dropdown // let project_state_filter: UseStateHandle> = use_state(|| OptionWrapper::new(Some(props.project_state.clone()))); // Dropdown let project_city_filter: UseStateHandle> = use_state(|| OptionWrapper::new(None)); // Dropdown let project_district_filter: UseStateHandle> = use_state(|| OptionWrapper::new(None)); //TODO: Think about price filtering /*// TextField let _project_min_price_filter: UseStateHandle> = use_state(|| OptionWrapper::new(None)); // TextField let _project_max_price_filter: UseStateHandle> = use_state(|| OptionWrapper::new(None));*/ let project_type_drop_down = comp_with::>>(DropDownProps { initial: OptionWrapper::new(None), options: vec![OptionWrapper::new(None), OptionWrapper::new(Some(ProjectType::Apartamento)), OptionWrapper::new(Some(ProjectType::Casa)), OptionWrapper::new(Some(ProjectType::Oficina)), OptionWrapper::new(Some(ProjectType::Local)), OptionWrapper::new(Some(ProjectType::Solar)) ], selection_changed: { let cloned_project_type_filter = project_type_filter.clone(); Callback::from(move |project_type: OptionWrapper| { info!("{}", project_type.to_string()); cloned_project_type_filter.set(project_type) } )}, class_css: Some("project-search-filter-item".into()) }); /* // TODO: Fix ProjectState tostring printing the db insertable let project_state_drop_down = comp_with::>>(DropDownProps { initial: (*project_state_filter).clone(), options: vec![OptionWrapper::new(Some(ProjectState::InConstruction)), OptionWrapper::new(Some(ProjectState::Finished)) ], selection_changed: { let cloned_project_state_filter = project_state_filter.clone(); Callback::from(move |project_state: OptionWrapper| { info!("{}", project_state.to_string()); cloned_project_state_filter.set(project_state) } )}, class_css: Some("project-search-filter-item".into()) });*/ let project_condition_drop_down = comp_with::>>(DropDownProps { initial: OptionWrapper::new(None), options: vec![OptionWrapper::new(None), OptionWrapper::new(Some(ProjectCondition::New)), OptionWrapper::new(Some(ProjectCondition::Resale)) ], selection_changed: { let cloned_project_condition_filter = project_condition_filter.clone(); Callback::from(move |project_condition: OptionWrapper| { info!("{}", project_condition.to_string()); cloned_project_condition_filter.set(project_condition) } )}, class_css: Some("project-search-filter-item".into()) }); let project_city_drop_down = comp_with::>>(DropDownProps { initial: OptionWrapper::new(None), options: (*cities_handle).clone(), selection_changed: { let cloned_project_city_filter = project_city_filter.clone(); let districts_handle = districts_handle.clone(); Callback::from(move |project_city: OptionWrapper| { let districts_handle = districts_handle.clone(); info!("{}", project_city.to_string()); cloned_project_city_filter.set(project_city.clone()); wasm_bindgen_futures::spawn_local(async move { match get_all_districts_in_city(&project_city.to_string()).await { Ok(districts) => { let mut districts_vec: Vec> = districts.into_iter().map(|district| OptionWrapper::new(Some(district))).collect(); districts_vec.insert(0, OptionWrapper::new(None)); districts_handle.set(districts_vec); }, Err(error) => info!("Error in dropdown callback: {}", error), }; }); } )}, class_css: Some("project-search-filter-item".into()) }); //TODO: District dropdown should only show districts in city, otherwise show nothing or disabled let project_district_drop_down = comp_with::>>(DropDownProps { initial: OptionWrapper::new(None), options: (*districts_handle).clone(), selection_changed: { let cloned_project_district_filter = project_district_filter.clone(); Callback::from(move |project_district: OptionWrapper| { info!("{}", project_district.to_string()); cloned_project_district_filter.set(project_district) } )}, class_css: Some("project-search-filter-item".into()) }); let search_onclick = { let search_results_handle = search_results_handle.clone(); let page_counter = page_counter.clone(); let project_type_filter = project_type_filter.clone(); let project_condition_filter = project_condition_filter.clone(); let project_city_filter = project_city_filter.clone(); let project_district_filter = project_district_filter.clone(); let props = props.clone(); Callback::from(move |_| { let mut filters = Vec::new(); if props.project_state.eq(&ProjectState::Finished) { filters.push(Filter::Finished); } match &(*project_type_filter).option { Some(project_type) => filters.push(Filter::ByProjectType(project_type.clone())), None => {}, }; match &(*project_condition_filter).option { Some(project_condition) => filters.push(Filter::ByProjectCondition(project_condition.clone())), None => {}, }; match &(*project_city_filter).option { Some(project_city) => filters.push(Filter::InCity(project_city.clone())), None => {}, }; match &(*project_district_filter).option { Some(project_district) => filters.push(Filter::InDistrict(project_district.clone())), None => {}, }; let search_results_handle = search_results_handle.clone(); let page_counter = page_counter.clone(); wasm_bindgen_futures::spawn_local(async move { match get_all_projects_with_filters_paged(&(*page_counter), filters).await { Ok(projects) => { search_results_handle.set(projects) }, Err(error) => info!("Error in loading projects: {error}"), }; info!("done"); }); })}; html!{ <>
// Filters
{"Tipo de Proyecto"}
{project_type_drop_down}
/*
{"Estatus de Proyecto"}
{project_state_drop_down}
*/
{"CondiciĆ³n de Proyecto"}
{project_condition_drop_down}
{"Ciudad"}
{project_city_drop_down}
{"Sector"}
{project_district_drop_down}
{ if *finished_loading { html!{
// Search Results Content {(*search_results_handle).clone().into_iter().map(|project| html!{}).collect::()}
} } else { html!{
// Search Results Content
} } }
} } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Properties)] pub struct SearchPageProperties { pub project_state: ProjectState, }