Finished Unit edit
This commit is contained in:
parent
65395552e0
commit
7177823b14
@ -1,15 +1,20 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use jl_types::{
|
use jl_types::{
|
||||||
domain::{agent::Agent, contact::Contact, count::Count, location::Location, project::Project, unit::Unit},
|
domain::{
|
||||||
|
agent::Agent, contact::Contact, count::Count, location::Location, project::Project,
|
||||||
|
unit::Unit,
|
||||||
|
},
|
||||||
dto::{
|
dto::{
|
||||||
filters::Filter,
|
filters::Filter,
|
||||||
item::Item,
|
item::Item,
|
||||||
listing::Listing,
|
listing::Listing,
|
||||||
payloads::{
|
payloads::{
|
||||||
|
agent::{NewAgentPayload, UpdateAgentPayload},
|
||||||
contact::ContactPayload,
|
contact::ContactPayload,
|
||||||
location::NewLocationPayload,
|
location::NewLocationPayload,
|
||||||
project::{NewProjectPayload, UpdateProjectPayload}, unit::UpdateUnitPayload, agent::{UpdateAgentPayload, NewAgentPayload},
|
project::{NewProjectPayload, UpdateProjectPayload},
|
||||||
|
unit::{UpdateUnitPayload, NewUnitPayload},
|
||||||
},
|
},
|
||||||
project_card::ProjectCardDto,
|
project_card::ProjectCardDto,
|
||||||
},
|
},
|
||||||
@ -213,6 +218,20 @@ pub async fn create_new_agent(agent: NewAgentPayload) -> Result<Agent, err::Erro
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn create_new_unit(unit: NewUnitPayload) -> Result<Unit, err::Error> {
|
||||||
|
perform_request_without_client(
|
||||||
|
BASE_URL.into(),
|
||||||
|
Method::POST,
|
||||||
|
format!("admin/unit"),
|
||||||
|
Some(unit),
|
||||||
|
200,
|
||||||
|
Vec::new(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn create_new_project(project: NewProjectPayload) -> Result<Project, err::Error> {
|
pub async fn create_new_project(project: NewProjectPayload) -> Result<Project, err::Error> {
|
||||||
perform_request_without_client(
|
perform_request_without_client(
|
||||||
BASE_URL.into(),
|
BASE_URL.into(),
|
||||||
|
@ -3,8 +3,9 @@ use yew::prelude::*;
|
|||||||
use yew_router::prelude::use_navigator;
|
use yew_router::prelude::use_navigator;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
api::backend::delete_agent,
|
||||||
pages::admin::edit::{EditItem, EditType},
|
pages::admin::edit::{EditItem, EditType},
|
||||||
routes::main_router::Route, api::backend::delete_agent,
|
routes::main_router::Route,
|
||||||
};
|
};
|
||||||
|
|
||||||
//TODO: Add admin tag
|
//TODO: Add admin tag
|
||||||
|
@ -4,8 +4,9 @@ use yew::prelude::*;
|
|||||||
use yew_router::prelude::use_navigator;
|
use yew_router::prelude::use_navigator;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
api::backend::delete_unit,
|
||||||
pages::admin::edit::{EditItem, EditType},
|
pages::admin::edit::{EditItem, EditType},
|
||||||
routes::main_router::Route, api::backend::delete_unit,
|
routes::main_router::Route,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[function_component(AdminUnit)]
|
#[function_component(AdminUnit)]
|
||||||
@ -89,5 +90,5 @@ pub fn admin_unit(props: &AdminUnitProps) -> Html {
|
|||||||
pub struct AdminUnitProps {
|
pub struct AdminUnitProps {
|
||||||
pub unit: Unit,
|
pub unit: Unit,
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
pub deletecb: Callback<usize>
|
pub deletecb: Callback<usize>,
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,6 @@ pub struct DropDownProps<T: Display + std::cmp::PartialEq + Clone> {
|
|||||||
pub onchange: Option<Callback<Option<T>>>,
|
pub onchange: Option<Callback<Option<T>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[function_component(DropDown)]
|
#[function_component(DropDown)]
|
||||||
pub fn dropdown<T: Display + std::cmp::PartialEq + Clone + 'static>(
|
pub fn dropdown<T: Display + std::cmp::PartialEq + Clone + 'static>(
|
||||||
props: &DropDownProps<T>,
|
props: &DropDownProps<T>,
|
||||||
@ -38,10 +36,10 @@ pub fn dropdown<T: Display + std::cmp::PartialEq + Clone + 'static>(
|
|||||||
},
|
},
|
||||||
selection_changed: match props.onchange.clone() {
|
selection_changed: match props.onchange.clone() {
|
||||||
Some(cb) => cb,
|
Some(cb) => cb,
|
||||||
None => Callback::from(|_| {})
|
None => Callback::from(|_| {}),
|
||||||
},
|
},
|
||||||
class_css: Some("admin-dropdown".into()),
|
class_css: Some("admin-dropdown".into()),
|
||||||
none_str: props.unpicked_text.clone()
|
none_str: props.unpicked_text.clone(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
//if (*props.selected).is_none() {selection_changed_cb.emit( OptionWrapper::new((*props.selected).clone()));}
|
//if (*props.selected).is_none() {selection_changed_cb.emit( OptionWrapper::new((*props.selected).clone()));}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use jl_types::domain::media::{Media, MediaList};
|
use jl_types::{domain::media::{Media, MediaList}, dto::item::Item};
|
||||||
use js_sys::Uint8Array;
|
use js_sys::Uint8Array;
|
||||||
use wasm_bindgen_futures::JsFuture;
|
use wasm_bindgen_futures::JsFuture;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
@ -12,7 +12,11 @@ use crate::{
|
|||||||
pub fn media_picker(props: &MediaPickerProps) -> Html {
|
pub fn media_picker(props: &MediaPickerProps) -> Html {
|
||||||
html! {
|
html! {
|
||||||
<>
|
<>
|
||||||
<div class={{"textfield-label-required"}}>{"Media del proyecto"}</div>
|
<div class={{"textfield-label-required"}}>{format!("Media {}", match props.item {
|
||||||
|
Item::Project => "del Proyecto",
|
||||||
|
Item::Unit => "de la Unidad",
|
||||||
|
Item::Agent => "del Agente"
|
||||||
|
})}</div>
|
||||||
<div class={"mediapicker-container"}>
|
<div class={"mediapicker-container"}>
|
||||||
<MediaListRendered medialist={props.value.clone()} onchange={props.onchange.clone()} item={props.item.clone()}/>
|
<MediaListRendered medialist={props.value.clone()} onchange={props.onchange.clone()} item={props.item.clone()}/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,7 +12,7 @@ pub mod media_picker;
|
|||||||
pub mod media_slideshow;
|
pub mod media_slideshow;
|
||||||
pub mod nav_bar;
|
pub mod nav_bar;
|
||||||
pub mod new_widget;
|
pub mod new_widget;
|
||||||
pub mod project_card;
|
|
||||||
pub mod textfield;
|
|
||||||
pub mod single_media_picker;
|
|
||||||
pub mod number_textfield;
|
pub mod number_textfield;
|
||||||
|
pub mod project_card;
|
||||||
|
pub mod single_media_picker;
|
||||||
|
pub mod textfield;
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
|
use std::{fmt::Display, str::FromStr};
|
||||||
use wasm_bindgen::{JsCast, UnwrapThrowExt};
|
use wasm_bindgen::{JsCast, UnwrapThrowExt};
|
||||||
use web_sys::{HtmlInputElement};
|
use web_sys::HtmlInputElement;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use std::{str::FromStr, fmt::{Display}};
|
|
||||||
|
|
||||||
#[derive(Properties, PartialEq, Clone)]
|
#[derive(Properties, PartialEq, Clone)]
|
||||||
pub struct NumberTextFieldProps<T: PartialEq + Clone + Display + Default + FromStr> {
|
pub struct NumberTextFieldProps<T: PartialEq + Clone + Display + Default + FromStr> {
|
||||||
@ -14,23 +13,26 @@ pub struct NumberTextFieldProps<T: PartialEq + Clone + Display + Default + FromS
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(NumberTextField)]
|
#[function_component(NumberTextField)]
|
||||||
pub fn number_textfield<T: PartialEq + Clone + Display + Default + FromStr + 'static>(props: &NumberTextFieldProps<T>) -> Html {
|
pub fn number_textfield<T: PartialEq + Clone + Display + Default + FromStr + 'static>(
|
||||||
|
props: &NumberTextFieldProps<T>,
|
||||||
|
) -> Html {
|
||||||
let on_input_changed = {
|
let on_input_changed = {
|
||||||
let handle = props.value.clone();
|
let handle = props.value.clone();
|
||||||
let onchange = props.onchange.clone();
|
let onchange = props.onchange.clone();
|
||||||
Callback::from(move |e: InputEvent| {
|
Callback::from(move |e: InputEvent| {
|
||||||
let value = match get_number_value_from_input_event::<T>(e) {
|
let value = match get_number_value_from_input_event::<T>(e) {
|
||||||
Ok(float) => float,
|
Ok(float) => float,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
log::error!("Error ocurred attempting to parse number on input type number. This only happens in firefox browsers.");
|
log::error!("Error ocurred attempting to parse number on input type number. This only happens in firefox browsers.");
|
||||||
T::default()
|
T::default()
|
||||||
}};
|
}
|
||||||
match onchange.clone() {
|
};
|
||||||
Some(onchange) => onchange.emit(value.clone()),
|
match onchange.clone() {
|
||||||
None => {}
|
Some(onchange) => onchange.emit(value.clone()),
|
||||||
};
|
None => {}
|
||||||
handle.set(value);
|
};
|
||||||
})
|
handle.set(value);
|
||||||
|
})
|
||||||
};
|
};
|
||||||
html! {
|
html! {
|
||||||
<div class={"textfield-container"}>
|
<div class={"textfield-container"}>
|
||||||
|
@ -2,10 +2,7 @@ use js_sys::Uint8Array;
|
|||||||
use wasm_bindgen_futures::JsFuture;
|
use wasm_bindgen_futures::JsFuture;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{api, components::textfield::get_files_from_input_event};
|
||||||
api,
|
|
||||||
components::textfield::{get_files_from_input_event},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(PartialEq, Properties, Clone)]
|
#[derive(PartialEq, Properties, Clone)]
|
||||||
pub struct SingleMediaPickerProps {
|
pub struct SingleMediaPickerProps {
|
||||||
@ -107,4 +104,3 @@ pub fn single_media_picker(props: &SingleMediaPickerProps) -> Html {
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,13 +5,9 @@ use yew::prelude::*;
|
|||||||
use yew_router::prelude::use_navigator;
|
use yew_router::prelude::use_navigator;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::backend::{
|
api::backend::{get_agent_with_id, get_project_listing},
|
||||||
get_project_listing, get_agent_with_id, get_unit_with_id,
|
components::admin_nav_bar::AdminNavigationBar,
|
||||||
},
|
pages::admin::fields::{agent::AgentFields, project::ProjectFields, unit::UnitFields},
|
||||||
components::{
|
|
||||||
admin_nav_bar::AdminNavigationBar,
|
|
||||||
},
|
|
||||||
pages::admin::{fields::{project::ProjectFields, unit::UnitFields, agent::AgentFields}},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// All of the editing actions of the admin panel will lead to here. This should take an id of anything. A unit, a project, an agent. And its corresponding ID.
|
/// All of the editing actions of the admin panel will lead to here. This should take an id of anything. A unit, a project, an agent. And its corresponding ID.
|
||||||
@ -20,11 +16,9 @@ pub fn edit_page(props: &AdminEditPageProps) -> Html {
|
|||||||
let _navigator = use_navigator().unwrap();
|
let _navigator = use_navigator().unwrap();
|
||||||
let listing = use_state(|| None);
|
let listing = use_state(|| None);
|
||||||
let agent = use_state(|| None);
|
let agent = use_state(|| None);
|
||||||
let unit = use_state(|| None);
|
|
||||||
use_state(|| {
|
use_state(|| {
|
||||||
let listing = listing.clone();
|
let listing = listing.clone();
|
||||||
let agent = agent.clone();
|
let agent = agent.clone();
|
||||||
let unit = unit.clone();
|
|
||||||
match props.edit_item {
|
match props.edit_item {
|
||||||
EditItem::Agent => {
|
EditItem::Agent => {
|
||||||
match props.edit_type {
|
match props.edit_type {
|
||||||
@ -61,17 +55,7 @@ pub fn edit_page(props: &AdminEditPageProps) -> Html {
|
|||||||
EditItem::Unit(_) => {
|
EditItem::Unit(_) => {
|
||||||
match props.edit_type {
|
match props.edit_type {
|
||||||
EditType::New => {}
|
EditType::New => {}
|
||||||
EditType::Existing(uid) => {
|
EditType::Existing(_) => {}
|
||||||
wasm_bindgen_futures::spawn_local(async move {
|
|
||||||
let unit_result = get_unit_with_id(&uid).await;
|
|
||||||
match unit_result {
|
|
||||||
Ok(unit_persisted) => {
|
|
||||||
unit.set(Some(unit_persisted));
|
|
||||||
}
|
|
||||||
Err(error) => log::error!("Error loading unit: {error}"),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -97,7 +81,7 @@ pub fn edit_page(props: &AdminEditPageProps) -> Html {
|
|||||||
match props.edit_item {
|
match props.edit_item {
|
||||||
EditItem::Agent => { html! { <AgentFields agent={(*agent).clone()} edittype={props.edit_type.clone()} /> } },
|
EditItem::Agent => { html! { <AgentFields agent={(*agent).clone()} edittype={props.edit_type.clone()} /> } },
|
||||||
EditItem::Project => { html! { <ProjectFields listing={(*listing).clone()} edittype={props.edit_type.clone()}/> } },
|
EditItem::Project => { html! { <ProjectFields listing={(*listing).clone()} edittype={props.edit_type.clone()}/> } },
|
||||||
EditItem::Unit(project_id) => { html! { <UnitFields edittype={props.edit_type.clone()} unit={(*unit).clone()} projectid={project_id}/> } },
|
EditItem::Unit(project_id) => { html! { <UnitFields edittype={props.edit_type.clone()} projectid={project_id}/> } },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,8 +1,18 @@
|
|||||||
use jl_types::{domain::{agent::Agent, credential::CredentialType}, dto::payloads::agent::{NewAgentPayload, UpdateAgentPayload}};
|
use jl_types::{
|
||||||
|
domain::{agent::Agent, credential::CredentialType},
|
||||||
|
dto::payloads::agent::{NewAgentPayload, UpdateAgentPayload},
|
||||||
|
};
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yew_router::prelude::use_navigator;
|
use yew_router::prelude::use_navigator;
|
||||||
|
|
||||||
use crate::{pages::admin::edit::{EditType}, components::{textfield::TextField, dropdown::DropDown, single_media_picker::SingleMediaPicker}, api::backend::{update_agent, create_new_agent}, routes::main_router::Route, };
|
use crate::{
|
||||||
|
api::backend::{create_new_agent, update_agent},
|
||||||
|
components::{
|
||||||
|
dropdown::DropDown, single_media_picker::SingleMediaPicker, textfield::TextField,
|
||||||
|
},
|
||||||
|
pages::admin::edit::EditType,
|
||||||
|
routes::main_router::Route,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Properties, PartialEq, Clone)]
|
#[derive(Properties, PartialEq, Clone)]
|
||||||
pub struct AgentFieldsProps {
|
pub struct AgentFieldsProps {
|
||||||
@ -47,14 +57,12 @@ pub fn agent_fields(props: &AgentFieldsProps) -> Html {
|
|||||||
None => String::new(),
|
None => String::new(),
|
||||||
});
|
});
|
||||||
profile_picture_url_handle.set(match agent_opt.clone() {
|
profile_picture_url_handle.set(match agent_opt.clone() {
|
||||||
Some(agent) => {
|
Some(agent) => agent.profile_picture_url,
|
||||||
agent.profile_picture_url
|
|
||||||
},
|
|
||||||
None => String::new(),
|
None => String::new(),
|
||||||
});
|
});
|
||||||
credential_type.set(match agent_opt.clone() {
|
credential_type.set(match agent_opt.clone() {
|
||||||
Some(agent) => Some(agent.credential_type),
|
Some(agent) => Some(agent.credential_type),
|
||||||
None => None
|
None => None,
|
||||||
});
|
});
|
||||||
credential_handle.set(match agent_opt.clone() {
|
credential_handle.set(match agent_opt.clone() {
|
||||||
Some(agent) => agent.credential,
|
Some(agent) => agent.credential,
|
||||||
@ -96,46 +104,46 @@ pub fn agent_fields(props: &AgentFieldsProps) -> Html {
|
|||||||
let agent_name = (*agent_name_handle).clone();
|
let agent_name = (*agent_name_handle).clone();
|
||||||
let profile_picture_url = (*profile_picture_url_handle).clone();
|
let profile_picture_url = (*profile_picture_url_handle).clone();
|
||||||
let credential = (*credential_handle).clone();
|
let credential = (*credential_handle).clone();
|
||||||
let credential_type = if let Some(credential_type) = (*credential_type_handle).clone(){
|
let credential_type =
|
||||||
credential_type
|
if let Some(credential_type) = (*credential_type_handle).clone() {
|
||||||
} else {
|
credential_type
|
||||||
log::error!("Missing field credential_type");
|
} else {
|
||||||
return;
|
log::error!("Missing field credential_type");
|
||||||
};
|
return;
|
||||||
|
};
|
||||||
match edit_type {
|
match edit_type {
|
||||||
EditType::New => {
|
EditType::New => {
|
||||||
let agent = NewAgentPayload {
|
let agent = NewAgentPayload {
|
||||||
credential,
|
credential,
|
||||||
credential_type,
|
credential_type,
|
||||||
full_name: agent_name,
|
full_name: agent_name,
|
||||||
profile_picture_url
|
profile_picture_url,
|
||||||
};
|
};
|
||||||
match create_new_agent(agent).await {
|
match create_new_agent(agent).await {
|
||||||
Ok(_) => navigator.push(&Route::AdminAgents),
|
Ok(_) => navigator.push(&Route::AdminAgents),
|
||||||
Err(error) => log::error!("Error updating agent: {error}")
|
Err(error) => log::error!("Error updating agent: {error}"),
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
EditType::Existing(id) => {
|
EditType::Existing(id) => {
|
||||||
let agent = UpdateAgentPayload {
|
let agent = UpdateAgentPayload {
|
||||||
id,
|
id,
|
||||||
credential: Some(credential),
|
credential: Some(credential),
|
||||||
credential_type: Some(credential_type),
|
credential_type: Some(credential_type),
|
||||||
full_name: Some(agent_name),
|
full_name: Some(agent_name),
|
||||||
profile_picture_url: Some(profile_picture_url)
|
profile_picture_url: Some(profile_picture_url),
|
||||||
};
|
};
|
||||||
match update_agent(agent).await {
|
match update_agent(agent).await {
|
||||||
Ok(_) => navigator.push(&Route::AdminAgents),
|
Ok(_) => navigator.push(&Route::AdminAgents),
|
||||||
Err(error) => log::error!("Error updating agent: {error}")
|
Err(error) => log::error!("Error updating agent: {error}"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
html! {
|
html! {
|
||||||
<>
|
<>
|
||||||
<SingleMediaPicker value={profile_picture_url_handle} onchange={ontype_cb.clone()} item={jl_types::dto::item::Item::Agent}/>
|
<SingleMediaPicker value={profile_picture_url_handle} item={jl_types::dto::item::Item::Agent} onchange={ontype_cb.clone()} />
|
||||||
<TextField label={"Nombre Completo"} value={agent_name_handle} required={true} onchange={ontype_cb.clone()}/>
|
<TextField label={"Nombre Completo"} value={agent_name_handle} required={true} onchange={ontype_cb.clone()}/>
|
||||||
|
|
||||||
{if (*credential_type).clone().is_none() {
|
{if (*credential_type).clone().is_none() {
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
|
pub mod agent;
|
||||||
pub mod project;
|
pub mod project;
|
||||||
pub mod unit;
|
pub mod unit;
|
||||||
pub mod agent;
|
|
@ -1,10 +1,39 @@
|
|||||||
use chrono::NaiveTime;
|
use chrono::NaiveTime;
|
||||||
use jl_types::{dto::{listing::Listing, payloads::{location::NewLocationPayload, project::{UpdateProjectPayload, NewProjectPayload}}}, domain::{agent::Agent, media::MediaList, unit::Unit, project_condition::ProjectCondition, project_type::ProjectType, project_state::ProjectState}};
|
use jl_types::{
|
||||||
|
domain::{
|
||||||
|
agent::Agent, media::MediaList, project_condition::ProjectCondition,
|
||||||
|
project_state::ProjectState, project_type::ProjectType, unit::Unit,
|
||||||
|
},
|
||||||
|
dto::{
|
||||||
|
listing::Listing,
|
||||||
|
payloads::{
|
||||||
|
location::NewLocationPayload,
|
||||||
|
project::{NewProjectPayload, UpdateProjectPayload},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use std::str::FromStr;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yew_router::prelude::use_navigator;
|
use yew_router::prelude::use_navigator;
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use crate::{pages::admin::{edit::{EditType, EditItem}, units::AdminUnits}, api::backend::{get_all_agents, get_location_with_city_and_district, create_location, create_new_project, update_project}, routes::main_router::Route, components::{new_widget::NewThingWidget, dropdown::DropDown, media_picker::MediaPicker, textfield::{TextField, TextFieldType}, datepicker::DatePicker}};
|
use crate::{
|
||||||
|
api::backend::{
|
||||||
|
create_location, create_new_project, get_all_agents, get_location_with_city_and_district,
|
||||||
|
update_project,
|
||||||
|
},
|
||||||
|
components::{
|
||||||
|
datepicker::DatePicker,
|
||||||
|
dropdown::DropDown,
|
||||||
|
media_picker::MediaPicker,
|
||||||
|
new_widget::NewThingWidget,
|
||||||
|
textfield::{TextField, TextFieldType},
|
||||||
|
},
|
||||||
|
pages::admin::{
|
||||||
|
edit::{EditItem, EditType},
|
||||||
|
units::AdminUnits,
|
||||||
|
},
|
||||||
|
routes::main_router::Route,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Properties, PartialEq, Clone)]
|
#[derive(Properties, PartialEq, Clone)]
|
||||||
pub struct ProjectFieldsProps {
|
pub struct ProjectFieldsProps {
|
||||||
@ -93,9 +122,7 @@ pub fn generate_fields_for_project(props: &ProjectFieldsProps) -> Html {
|
|||||||
});
|
});
|
||||||
project_state.set(match listing_opt.clone() {
|
project_state.set(match listing_opt.clone() {
|
||||||
Some(listing) => Some(listing.project.project_state),
|
Some(listing) => Some(listing.project.project_state),
|
||||||
None => {
|
None => None,
|
||||||
None
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
project_condition.set(match listing_opt.clone() {
|
project_condition.set(match listing_opt.clone() {
|
||||||
Some(listing) => Some(listing.project.project_condition),
|
Some(listing) => Some(listing.project.project_condition),
|
||||||
|
@ -1,36 +1,253 @@
|
|||||||
use jl_types::domain::{unit::Unit, unit_type::UnitType};
|
use jl_types::{domain::{media::MediaList, unit_type::UnitType}, dto::payloads::unit::{NewUnitPayload, UpdateUnitPayload}};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yew_router::prelude::use_navigator;
|
use yew_router::prelude::use_navigator;
|
||||||
|
|
||||||
use crate::{pages::admin::edit::EditType, components::number_textfield::NumberTextField};
|
use crate::{
|
||||||
|
components::{dropdown::DropDown, number_textfield::NumberTextField, media_picker::MediaPicker, textfield::{TextField, TextFieldType}},
|
||||||
|
pages::admin::edit::EditType, api::backend::{get_unit_with_id, create_new_unit, update_unit}, routes::main_router::Route,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Properties, PartialEq, Clone)]
|
#[derive(Properties, PartialEq, Clone)]
|
||||||
pub struct UnitFieldsProps {
|
pub struct UnitFieldsProps {
|
||||||
pub unit: Option<Unit>,
|
|
||||||
pub edittype: EditType,
|
pub edittype: EditType,
|
||||||
pub projectid: Option<Uuid>,
|
pub projectid: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(UnitFields)]
|
#[function_component(UnitFields)]
|
||||||
pub fn unit_fields(props: &UnitFieldsProps) -> Html {
|
pub fn unit_fields(props: &UnitFieldsProps) -> Html {
|
||||||
|
|
||||||
|
let unit_opt = use_state(|| None);
|
||||||
|
{
|
||||||
|
let unit_opt = unit_opt.clone();
|
||||||
|
use_state(move || {
|
||||||
|
if let EditType::Existing(id) = props.edittype.clone() {
|
||||||
|
wasm_bindgen_futures::spawn_local(async move {
|
||||||
|
let unit_result = get_unit_with_id(&id).await;
|
||||||
|
match unit_result {
|
||||||
|
Ok(unit_persisted) => {
|
||||||
|
unit_opt.set(Some(unit_persisted));
|
||||||
|
}
|
||||||
|
Err(error) => log::error!("Error loading unit: {error}"),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let navigator = use_navigator().unwrap();
|
let navigator = use_navigator().unwrap();
|
||||||
let user_typed = use_state(|| false);
|
let user_typed = use_state(|| false);
|
||||||
|
let edit_type = props.edittype.clone();
|
||||||
|
let project_id = props.projectid.clone();
|
||||||
|
|
||||||
|
let ontype_cb = {
|
||||||
|
let user_typed = user_typed.clone();
|
||||||
|
Callback::from(move |_: String| {
|
||||||
|
user_typed.set(true);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let onselect_cb = {
|
||||||
|
let user_typed = user_typed.clone();
|
||||||
|
Callback::from(move |_| {
|
||||||
|
user_typed.set(true);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let onchange_number_cb = {
|
||||||
|
let user_typed = user_typed.clone();
|
||||||
|
Callback::from(move |_| {
|
||||||
|
user_typed.set(true);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let onchange_number_cb_2 = {
|
||||||
|
let user_typed = user_typed.clone();
|
||||||
|
Callback::from(move |_| {
|
||||||
|
user_typed.set(true);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let onchange_number_cb_3 = {
|
||||||
|
let user_typed = user_typed.clone();
|
||||||
|
Callback::from(move |_| {
|
||||||
|
user_typed.set(true);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
// Fields
|
// Fields
|
||||||
let price_usd = use_state(|| 0.0);
|
let price_usd = use_state_eq(|| 0.0);
|
||||||
let unit_type_handle: UseStateHandle<Option<UnitType>> = use_state(|| None);
|
let unit_type_handle: UseStateHandle<Option<UnitType>> = use_state_eq(|| None);
|
||||||
let rooms_handle: UseStateHandle<i16> = use_state(|| 0);
|
let rooms_handle: UseStateHandle<i16> = use_state_eq(|| 0);
|
||||||
let bathrooms_handle: UseStateHandle<i16> = use_state(|| 0);
|
let bathrooms_handle: UseStateHandle<i16> = use_state_eq(|| 0);
|
||||||
let area_handle: UseStateHandle<f32> = use_state(|| 0.0);
|
let area_handle: UseStateHandle<f32> = use_state_eq(|| 0.0);
|
||||||
let a = 0.0;
|
let media_handle: UseStateHandle<MediaList> = use_state_eq(|| MediaList {
|
||||||
let b = a as i32;
|
media_list: Vec::new(),
|
||||||
|
});
|
||||||
|
let unit_admin_tag_handle: UseStateHandle<String> = use_state_eq(|| String::new());
|
||||||
|
let unit_description_handle = use_state_eq(|| String::new());
|
||||||
|
|
||||||
|
if !*user_typed {
|
||||||
|
price_usd.set(match (*unit_opt).clone() {
|
||||||
|
Some(unit) => unit.price_usd,
|
||||||
|
None => 0.0,
|
||||||
|
});
|
||||||
|
unit_type_handle.set(match (*unit_opt).clone() {
|
||||||
|
Some(unit) => Some(unit.unit_type),
|
||||||
|
None => None
|
||||||
|
});
|
||||||
|
media_handle.set(match (*unit_opt).clone() {
|
||||||
|
Some(unit) => unit.media,
|
||||||
|
None => MediaList {
|
||||||
|
media_list: Vec::new(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
rooms_handle.set(match (*unit_opt).clone() {
|
||||||
|
Some(unit) => unit.rooms,
|
||||||
|
None => 0
|
||||||
|
});
|
||||||
|
bathrooms_handle.set(match (*unit_opt).clone() {
|
||||||
|
Some(unit) => unit.bathrooms,
|
||||||
|
None => 0
|
||||||
|
});
|
||||||
|
area_handle.set(match (*unit_opt).clone() {
|
||||||
|
Some(unit) => unit.area,
|
||||||
|
None => 0.0
|
||||||
|
});
|
||||||
|
unit_description_handle.set(match (*unit_opt).clone() {
|
||||||
|
Some(unit) => unit.description,
|
||||||
|
None => String::new(),
|
||||||
|
});
|
||||||
|
unit_admin_tag_handle.set(match (*unit_opt).clone() {
|
||||||
|
Some(unit) => match unit.admin_tag {
|
||||||
|
Some(admin_tag) => admin_tag,
|
||||||
|
None => String::new()
|
||||||
|
},
|
||||||
|
None => String::new(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let update_button_onclick = {
|
||||||
|
let navigator = navigator.clone();
|
||||||
|
let edit_type = edit_type.clone();
|
||||||
|
let project_id = project_id.clone();
|
||||||
|
|
||||||
|
let price_usd = price_usd.clone();
|
||||||
|
let unit_type_handle: UseStateHandle<Option<UnitType>> = unit_type_handle.clone();
|
||||||
|
let rooms_handle: UseStateHandle<i16> = rooms_handle.clone();
|
||||||
|
let bathrooms_handle: UseStateHandle<i16> = bathrooms_handle.clone();
|
||||||
|
let area_handle: UseStateHandle<f32> = area_handle.clone();
|
||||||
|
let media_handle: UseStateHandle<MediaList> = media_handle.clone();
|
||||||
|
let unit_admin_tag_handle: UseStateHandle<String> = unit_admin_tag_handle.clone();
|
||||||
|
let unit_description_handle = unit_description_handle.clone();
|
||||||
|
Callback::from(move |_: MouseEvent| {
|
||||||
|
let navigator = navigator.clone();
|
||||||
|
let edit_type = edit_type.clone();
|
||||||
|
let project_id = project_id.clone();
|
||||||
|
|
||||||
|
let price_usd = price_usd.clone();
|
||||||
|
let unit_type_handle: UseStateHandle<Option<UnitType>> = unit_type_handle.clone();
|
||||||
|
let rooms_handle: UseStateHandle<i16> = rooms_handle.clone();
|
||||||
|
let bathrooms_handle: UseStateHandle<i16> = bathrooms_handle.clone();
|
||||||
|
let area_handle: UseStateHandle<f32> = area_handle.clone();
|
||||||
|
let media_handle: UseStateHandle<MediaList> = media_handle.clone();
|
||||||
|
let unit_admin_tag_handle: UseStateHandle<String> = unit_admin_tag_handle.clone();
|
||||||
|
let unit_description_handle = unit_description_handle.clone();
|
||||||
|
wasm_bindgen_futures::spawn_local(async move {
|
||||||
|
let price_usd = (*price_usd).clone();
|
||||||
|
let unit_type = match (*unit_type_handle).clone() {
|
||||||
|
Some(unit_type) => unit_type,
|
||||||
|
None => {
|
||||||
|
log::error!("Missing field unit_type");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let rooms = (*rooms_handle).clone();
|
||||||
|
let bathrooms = (*bathrooms_handle).clone();
|
||||||
|
let area = (*area_handle).clone();
|
||||||
|
let media = (*media_handle).clone();
|
||||||
|
let unit_admin_tag_val = (*unit_admin_tag_handle).clone();
|
||||||
|
let admin_tag = if unit_admin_tag_val.is_empty() { None } else { Some(unit_admin_tag_val) };
|
||||||
|
let description = (*unit_description_handle).clone();
|
||||||
|
|
||||||
|
match edit_type {
|
||||||
|
EditType::New => {
|
||||||
|
let payload = NewUnitPayload {
|
||||||
|
project_id,
|
||||||
|
price_usd,
|
||||||
|
unit_type,
|
||||||
|
rooms,
|
||||||
|
bathrooms,
|
||||||
|
area,
|
||||||
|
admin_tag,
|
||||||
|
media,
|
||||||
|
description
|
||||||
|
};
|
||||||
|
match create_new_unit(payload).await {
|
||||||
|
Ok(_) => navigator.clone().push(&Route::AdminProjects),
|
||||||
|
Err(error) => log::error!("Error creating new unit: {error}"),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
EditType::Existing(id) => {
|
||||||
|
let payload = UpdateUnitPayload {
|
||||||
|
id,
|
||||||
|
price_usd: Some(price_usd),
|
||||||
|
unit_type: Some(unit_type),
|
||||||
|
rooms: Some(rooms),
|
||||||
|
bathrooms: Some(bathrooms),
|
||||||
|
area: Some(area),
|
||||||
|
admin_tag: Some(admin_tag),
|
||||||
|
media: Some(media),
|
||||||
|
description: Some(description)
|
||||||
|
};
|
||||||
|
match update_unit(payload).await {
|
||||||
|
Ok(_) => navigator.clone().push(&Route::AdminProjects),
|
||||||
|
Err(error) => log::error!("Error creating new unit: {error}"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
})
|
||||||
|
};
|
||||||
html! {
|
html! {
|
||||||
<>
|
<>
|
||||||
<NumberTextField<f64> label={String::from("Precio en USD")} required={true} value={price_usd.clone()}/>
|
<NumberTextField<f64> label={String::from("Precio en USD")} required={true} value={price_usd.clone()} onchange={onchange_number_cb}/>
|
||||||
<NumberTextField<f32> label={String::from("Area en m^2")} required={true} value={area_handle.clone()}/>
|
<NumberTextField<f32> label={String::from("Area en m^2")} required={true} value={area_handle.clone()} onchange={onchange_number_cb_2}/>
|
||||||
<NumberTextField<i16> label={String::from("Cant. de habitaciones")} required={true} value={rooms_handle.clone()}/>
|
<NumberTextField<i16> label={String::from("Cant. de habitaciones")} required={true} value={rooms_handle.clone()} onchange={onchange_number_cb_3.clone()}/>
|
||||||
<NumberTextField<i16> label={String::from("Cant. de baños")} required={true} value={bathrooms_handle.clone()}/>
|
<NumberTextField<i16> label={String::from("Cant. de baños")} required={true} value={bathrooms_handle.clone()} onchange={onchange_number_cb_3}/>
|
||||||
|
<MediaPicker value={media_handle} onchange={ontype_cb.clone()} item={jl_types::dto::item::Item::Unit}/>
|
||||||
|
|
||||||
|
// Unit type dropdown
|
||||||
|
{if (*unit_type_handle).clone().is_none() {
|
||||||
|
match props.edittype.clone() {
|
||||||
|
EditType::New => html! {
|
||||||
|
<div class={"textfield-container"}>
|
||||||
|
<div class={"textfield-label-required"}>{"Tipo de Unidad"}</div>
|
||||||
|
<DropDown<UnitType> selected={unit_type_handle} options={vec![UnitType::ForSale, UnitType::NotForSale] } onchange={onselect_cb} />
|
||||||
|
</div>
|
||||||
|
},
|
||||||
|
EditType::Existing(_) => if *user_typed {
|
||||||
|
html! {
|
||||||
|
<div class={"textfield-container"}>
|
||||||
|
<div class={"textfield-label-required"}>{"Tipo de Unidad"}</div>
|
||||||
|
<DropDown<UnitType> selected={unit_type_handle} options={vec![UnitType::ForSale, UnitType::NotForSale] } onchange={onselect_cb} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
html! {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
html! {
|
||||||
|
<div class={"textfield-container"}>
|
||||||
|
<div class={"textfield-label-required"}>{"Tipo de Unidad"}</div>
|
||||||
|
<DropDown<UnitType> selected={unit_type_handle} options={vec![UnitType::ForSale, UnitType::NotForSale] } onchange={onselect_cb} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<TextField label={"Descripción"} value={unit_description_handle} required={false} onchange={ontype_cb.clone()} fieldtype={TextFieldType::TextArea} />
|
||||||
|
<TextField label={"Comentario interno"} value={unit_admin_tag_handle} required={false} onchange={ontype_cb.clone()} />
|
||||||
|
<div class={"admin-edit-submit-button"} onclick={update_button_onclick}>
|
||||||
|
{"Actualizar"}
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
pub mod agents;
|
pub mod agents;
|
||||||
pub mod contacts;
|
pub mod contacts;
|
||||||
pub mod edit;
|
pub mod edit;
|
||||||
|
pub mod fields;
|
||||||
pub mod login;
|
pub mod login;
|
||||||
pub mod projects;
|
pub mod projects;
|
||||||
pub mod start;
|
pub mod start;
|
||||||
pub mod units;
|
pub mod units;
|
||||||
pub mod fields;
|
|
@ -5,7 +5,6 @@ use crate::components::admin_unit::AdminUnit;
|
|||||||
|
|
||||||
#[function_component(AdminUnits)]
|
#[function_component(AdminUnits)]
|
||||||
pub fn admin_units(props: &AdminUnitProps) -> Html {
|
pub fn admin_units(props: &AdminUnitProps) -> Html {
|
||||||
|
|
||||||
let units_handle = props.units.clone();
|
let units_handle = props.units.clone();
|
||||||
html! {
|
html! {
|
||||||
<div class={"admin-start-container"} style={"min-height: 10vh; margin-top: 10vh;"}>
|
<div class={"admin-start-container"} style={"min-height: 10vh; margin-top: 10vh;"}>
|
||||||
|
@ -10,7 +10,9 @@ use crate::{
|
|||||||
api::backend::{
|
api::backend::{
|
||||||
get_all_cities, get_all_districts_in_city, get_all_projects_with_filters_paged,
|
get_all_cities, get_all_districts_in_city, get_all_projects_with_filters_paged,
|
||||||
},
|
},
|
||||||
components::{footer::PageFooter, nav_bar::NavigationBar, project_card::ProjectCard, dropdown::DropDown},
|
components::{
|
||||||
|
dropdown::DropDown, footer::PageFooter, nav_bar::NavigationBar, project_card::ProjectCard,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[function_component(SearchPage)]
|
#[function_component(SearchPage)]
|
||||||
@ -31,10 +33,7 @@ pub fn search_page() -> Html {
|
|||||||
wasm_bindgen_futures::spawn_local(async move {
|
wasm_bindgen_futures::spawn_local(async move {
|
||||||
match get_all_cities().await {
|
match get_all_cities().await {
|
||||||
Ok(cities) => {
|
Ok(cities) => {
|
||||||
let cities: Vec<String> = cities
|
let cities: Vec<String> = cities.into_iter().map(|location| location).collect();
|
||||||
.into_iter()
|
|
||||||
.map(|location| location)
|
|
||||||
.collect();
|
|
||||||
cities_handle.set(cities);
|
cities_handle.set(cities);
|
||||||
}
|
}
|
||||||
Err(error) => log::error!("Error in loading cities: {error}"),
|
Err(error) => log::error!("Error in loading cities: {error}"),
|
||||||
@ -55,19 +54,14 @@ pub fn search_page() -> Html {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Dropdown
|
// Dropdown
|
||||||
let project_type_filter: UseStateHandle<Option<ProjectType>> =
|
let project_type_filter: UseStateHandle<Option<ProjectType>> = use_state(|| None);
|
||||||
use_state(|| None);
|
|
||||||
// Dropdown
|
// Dropdown
|
||||||
let project_condition_filter: UseStateHandle<Option<ProjectCondition>> =
|
let project_condition_filter: UseStateHandle<Option<ProjectCondition>> = use_state(|| None);
|
||||||
use_state(|| None);
|
|
||||||
// Dropdown
|
// Dropdown
|
||||||
let project_city_filter: UseStateHandle<Option<String>> =
|
let project_city_filter: UseStateHandle<Option<String>> = use_state(|| None);
|
||||||
use_state(|| None);
|
|
||||||
// Dropdown
|
// Dropdown
|
||||||
let project_district_filter: UseStateHandle<Option<String>> =
|
let project_district_filter: UseStateHandle<Option<String>> = use_state(|| None);
|
||||||
use_state(|| None);
|
let unit_rooms_filter: UseStateHandle<Option<usize>> = use_state(|| None);
|
||||||
let unit_rooms_filter: UseStateHandle<Option<usize>> =
|
|
||||||
use_state(|| None);
|
|
||||||
|
|
||||||
let search_onclick = {
|
let search_onclick = {
|
||||||
let search_results_handle = search_results_handle.clone();
|
let search_results_handle = search_results_handle.clone();
|
||||||
|
Loading…
Reference in New Issue
Block a user