265 lines
12 KiB
Rust
265 lines
12 KiB
Rust
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<Vec<ProjectCardDto>> = use_state(|| Vec::new());
|
|
let page_counter: UseStateHandle<i64> = 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<OptionWrapper<String>> = 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<OptionWrapper<ProjectType>> = use_state(|| OptionWrapper::new(None));
|
|
// Dropdown
|
|
let project_condition_filter: UseStateHandle<OptionWrapper<ProjectCondition>> = use_state(|| OptionWrapper::new(None));
|
|
// Dropdown
|
|
// let project_state_filter: UseStateHandle<OptionWrapper<ProjectState>> = use_state(|| OptionWrapper::new(Some(props.project_state.clone())));
|
|
// Dropdown
|
|
let project_city_filter: UseStateHandle<OptionWrapper<String>> = use_state(|| OptionWrapper::new(None));
|
|
// Dropdown
|
|
let project_district_filter: UseStateHandle<OptionWrapper<String>> = use_state(|| OptionWrapper::new(None));
|
|
//TODO: Think about price filtering
|
|
/*// TextField
|
|
let _project_min_price_filter: UseStateHandle<OptionWrapper<f64>> = use_state(|| OptionWrapper::new(None));
|
|
// TextField
|
|
let _project_max_price_filter: UseStateHandle<OptionWrapper<f64>> = use_state(|| OptionWrapper::new(None));*/
|
|
|
|
|
|
let project_type_drop_down = comp_with::<DropDown<OptionWrapper<ProjectType>>>(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<ProjectType>| {
|
|
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::<DropDown<OptionWrapper<ProjectState>>>(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<ProjectState>| {
|
|
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::<DropDown<OptionWrapper<ProjectCondition>>>(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<ProjectCondition>| {
|
|
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::<DropDown<OptionWrapper<String>>>(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<String>| {
|
|
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<OptionWrapper<String>> = 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::<DropDown<OptionWrapper<String>>>(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<String>| {
|
|
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!{
|
|
<>
|
|
<NavigationBar/>
|
|
<div class={"page-container"}>
|
|
<div class={"project-search-container"}>
|
|
<div class={"project-search-filters-container"}> // Filters
|
|
<div class={"project-search-filter-container"}>
|
|
<div class={"project-search-filter-label"}>
|
|
{"Tipo de Proyecto"}
|
|
</div>
|
|
{project_type_drop_down}
|
|
</div>
|
|
|
|
/*<div class={"project-search-filter-container"}>
|
|
<div class={"project-search-filter-label"}>
|
|
{"Estatus de Proyecto"}
|
|
</div>
|
|
{project_state_drop_down}
|
|
</div>*/
|
|
|
|
<div class={"project-search-filter-container"}>
|
|
<div class={"project-search-filter-label"}>
|
|
{"Condición de Proyecto"}
|
|
</div>
|
|
{project_condition_drop_down}
|
|
</div>
|
|
|
|
<div class={"project-search-filter-container"}>
|
|
<div class={"project-search-filter-label"}>
|
|
{"Ciudad"}
|
|
</div>
|
|
{project_city_drop_down}
|
|
</div>
|
|
|
|
<div class={"project-search-filter-container"}>
|
|
<div class={"project-search-filter-label"}>
|
|
{"Sector"}
|
|
</div>
|
|
{project_district_drop_down}
|
|
</div>
|
|
|
|
<button class={"project-search-button"} onclick={search_onclick}>
|
|
{"Buscar"}
|
|
</button>
|
|
</div>
|
|
<div class={"project-search-divider"}/>
|
|
</div>
|
|
|
|
{
|
|
if *finished_loading {
|
|
html!{
|
|
<div class={"project-search-results-container"}> // Search Results Content
|
|
{(*search_results_handle).clone().into_iter().map(|project| html!{<ProjectCard {project}/>}).collect::<Html>()}
|
|
</div>
|
|
}
|
|
} else {
|
|
html!{
|
|
<div class={"project-search-results-container"}> // Search Results Content
|
|
<div class="lds-facebook"><div></div><div></div><div></div></div>
|
|
</div>
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
</div>
|
|
</>
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Properties)]
|
|
pub struct SearchPageProperties {
|
|
pub project_state: ProjectState,
|
|
}
|