a
This commit is contained in:
parent
33f55a04a2
commit
c693287063
0
css/admin-agents.css
Normal file
0
css/admin-agents.css
Normal file
0
css/admin-contacts.css
Normal file
0
css/admin-contacts.css
Normal file
9
css/admin-projects.css
Normal file
9
css/admin-projects.css
Normal file
@ -0,0 +1,9 @@
|
||||
.admin-projects-table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: space-evenly;
|
||||
gap: 5px;
|
||||
|
||||
width: 90%;
|
||||
}
|
60
css/components/admin_project.css
Normal file
60
css/components/admin_project.css
Normal file
@ -0,0 +1,60 @@
|
||||
.admin-project-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
}
|
||||
.admin-project-container:hover {
|
||||
cursor: pointer;
|
||||
background-color: #02104a51;
|
||||
border-radius: 3px;
|
||||
transition-duration: 0.3s;
|
||||
}
|
||||
.admin-project-trash-bin {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
}
|
||||
.admin-project-trash-bin-selected {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
border-radius: 3px;
|
||||
font-size: 13px;
|
||||
background-color: #e32;
|
||||
color: white;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
}
|
||||
.admin-project-trash-bin:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.admin-project-location {
|
||||
font-size: 13px;
|
||||
font-family: Source Sans Pro;
|
||||
font-weight: 100;
|
||||
text-align: start;
|
||||
width: 150px;
|
||||
}
|
||||
.admin-project-column {
|
||||
font-size: 13px;
|
||||
font-family: Source Sans Pro;
|
||||
font-weight: 100;
|
||||
text-align: start;
|
||||
width: 100px;
|
||||
}
|
||||
.admin-project-index {
|
||||
font-size: 13px;
|
||||
font-family: Source Sans Pro;
|
||||
font-weight: 100;
|
||||
text-align: start;
|
||||
width: 15px;
|
||||
}
|
24
css/edit.css
Normal file
24
css/edit.css
Normal file
@ -0,0 +1,24 @@
|
||||
.admin-project-fields-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.admin-project-field-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.admin-project-field-label {
|
||||
font-family: Source Sans Pro;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
@ -15,6 +15,10 @@
|
||||
<link data-trunk type="text/css" href="css/admin-login.css" rel="css" />
|
||||
<link data-trunk type="text/css" href="css/admin-start.css" rel="css" />
|
||||
<link data-trunk type="text/css" href="css/admin-common.css" rel="css" />
|
||||
<link data-trunk type="text/css" href="css/admin-projects.css" rel="css" />
|
||||
<link data-trunk type="text/css" href="css/admin-agents.css" rel="css" />
|
||||
<link data-trunk type="text/css" href="css/admin-contacts.css" rel="css" />
|
||||
<link data-trunk type="text/css" href="css/edit.css" rel="css" />
|
||||
|
||||
|
||||
<link data-trunk type="text/css" href="css/components/loading.css" rel="css" />
|
||||
@ -23,6 +27,7 @@
|
||||
<link data-trunk type="text/css" href="css/components/footer.css" rel="css" />
|
||||
<link data-trunk type="text/css" href="css/components/agent_card.css" rel="css" />
|
||||
<link data-trunk type="text/css" href="css/components/admin_nav_bar.css" rel="css" />
|
||||
<link data-trunk type="text/css" href="css/components/admin_project.css" rel="css" />
|
||||
|
||||
|
||||
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
|
||||
|
62
src/components/admin_project.rs
Normal file
62
src/components/admin_project.rs
Normal file
@ -0,0 +1,62 @@
|
||||
use jl_types::{dto::project_card::ProjectCardDto, domain::project_state::ProjectState};
|
||||
use yew::prelude::*;
|
||||
use yew_router::{prelude::use_navigator};
|
||||
|
||||
use crate::{routes::main_router::Route, pages::admin::edit::{EditType, EditItem}};
|
||||
|
||||
#[function_component(AdminProject)]
|
||||
pub fn admin_project(props: &AdminProjectProps) -> Html {
|
||||
let navigator = use_navigator().unwrap();
|
||||
let location_formatted = format!("{}, {}", props.project.city, props.project.district);
|
||||
let is_attempting_delete = use_state(|| false);
|
||||
let delete_project = {
|
||||
let is_attempting_delete = is_attempting_delete.clone();
|
||||
Callback::from(move |event: MouseEvent| {
|
||||
if *is_attempting_delete {
|
||||
// Call delete
|
||||
is_attempting_delete.set(false);
|
||||
} else {
|
||||
is_attempting_delete.set(true);
|
||||
}
|
||||
event.stop_propagation();
|
||||
})
|
||||
};
|
||||
let onclick_item = {
|
||||
let props = props.clone();
|
||||
Callback::from(move |_| {
|
||||
navigator.push(&Route::AdminEdit { edit_type: EditType::Existing(props.project.id), edit_item: EditItem::Project });
|
||||
})
|
||||
};
|
||||
html! {
|
||||
<div class={"admin-project-container"} onclick={onclick_item}>
|
||||
<div class={"admin-project-index"}>
|
||||
{props.index + 1}
|
||||
</div>
|
||||
<div class={"admin-project-location"}>
|
||||
{location_formatted}
|
||||
</div>
|
||||
<div class={"admin-project-column"}>
|
||||
{props.project.project_type}
|
||||
</div>
|
||||
<div class={"admin-project-column"}>
|
||||
{props.project.project_condition.clone()}
|
||||
</div>
|
||||
<div class={"admin-project-column"}>
|
||||
{match props.project.project_state {
|
||||
ProjectState::InConstruction => "En Construcción",
|
||||
ProjectState::Finished => "Terminado",
|
||||
}}
|
||||
</div>
|
||||
<div class={if *is_attempting_delete {"admin-project-trash-bin-selected"} else {"admin-project-trash-bin"}} onclick={delete_project}>
|
||||
<i class="fa-regular fa-trash-can"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Properties, Clone)]
|
||||
pub struct AdminProjectProps {
|
||||
pub project: ProjectCardDto,
|
||||
pub index: usize,
|
||||
}
|
@ -5,4 +5,5 @@ pub mod floating_widget;
|
||||
pub mod feature;
|
||||
pub mod footer;
|
||||
pub mod agent_card;
|
||||
pub mod admin_nav_bar;
|
||||
pub mod admin_nav_bar;
|
||||
pub mod admin_project;
|
@ -1,15 +1,30 @@
|
||||
use yew::prelude::*;
|
||||
|
||||
use crate::components::admin_nav_bar::AdminNavigationBar;
|
||||
use crate::{components::admin_nav_bar::AdminNavigationBar, api::backend::get_all_agents};
|
||||
|
||||
#[function_component(AdminAgents)]
|
||||
pub fn admin_agents() -> Html {
|
||||
|
||||
let agents = use_state(|| Vec::new());
|
||||
|
||||
use_state(|| {
|
||||
let agents_handle = agents.clone();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
match get_all_agents().await {
|
||||
Ok(agents) => {
|
||||
agents_handle.set(agents);
|
||||
},
|
||||
Err(error) => log::error!("Error retrieving agents from backend. Error: {}", error),
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
html! {
|
||||
<>
|
||||
<AdminNavigationBar/>
|
||||
<div class={"page-container"}>
|
||||
<div class={"admin-page-container"}>
|
||||
<div class={"admin-start-container"}>
|
||||
{""}
|
||||
<div class={"admin-panel-page-title"}>{"Agentes"}</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
@ -1,15 +1,29 @@
|
||||
use yew::prelude::*;
|
||||
|
||||
use crate::components::admin_nav_bar::AdminNavigationBar;
|
||||
use crate::{components::admin_nav_bar::AdminNavigationBar, api::backend::get_all_contacts};
|
||||
|
||||
#[function_component(AdminContacts)]
|
||||
pub fn admin_contacts() -> Html {
|
||||
let contacts = use_state(|| Vec::new());
|
||||
|
||||
use_state(|| {
|
||||
let contacts_handle = contacts.clone();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
match get_all_contacts().await {
|
||||
Ok(contacts) => {
|
||||
contacts_handle.set(contacts);
|
||||
},
|
||||
Err(error) => log::error!("Error retrieving contacts from backend. Error: {}", error),
|
||||
};
|
||||
});
|
||||
});
|
||||
html! {
|
||||
<>
|
||||
<AdminNavigationBar/>
|
||||
<div class={"page-container"}>
|
||||
<div class={"admin-page-container"}>
|
||||
<div class={"admin-start-container"}>
|
||||
{""}
|
||||
<div class={"admin-panel-page-title"}>{"Solicitudes de Contacto"}</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
174
src/pages/admin/edit.rs
Normal file
174
src/pages/admin/edit.rs
Normal file
@ -0,0 +1,174 @@
|
||||
use std::{fmt::Display, str::FromStr};
|
||||
|
||||
use chrono::Utc;
|
||||
use jl_types::{ domain::{agent::Agent, project_state::ProjectState, project_condition::ProjectCondition, project_type::ProjectType, media::{Media, MediaList}, unit::Unit}, dto::listing::Listing};
|
||||
use uuid::Uuid;
|
||||
use yew::prelude::*;
|
||||
use yew_router::prelude::use_navigator;
|
||||
|
||||
use crate::{components::admin_nav_bar::AdminNavigationBar, api::backend::get_project_listing};
|
||||
|
||||
/// 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.
|
||||
#[function_component(AdminEditPage)]
|
||||
pub fn edit_page(props: &AdminEditPageProps) -> Html {
|
||||
let navigator = use_navigator().unwrap();
|
||||
let listing = use_state(|| None);
|
||||
|
||||
use_state(|| {
|
||||
let listing = listing.clone();
|
||||
match props.edit_item {
|
||||
EditItem::Agent => {
|
||||
|
||||
},
|
||||
EditItem::Project => {
|
||||
match props.edit_type {
|
||||
EditType::New => {},
|
||||
EditType::Existing(uid) => {
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
let listing_result = get_project_listing(&uid).await;
|
||||
match listing_result {
|
||||
Ok(listing_persisted) => {
|
||||
listing.set(Some(listing_persisted));
|
||||
},
|
||||
Err(error) => log::error!("Error loading listing: {error}")
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
EditItem::Unit(_) => {},
|
||||
}});
|
||||
|
||||
html! {
|
||||
<>
|
||||
<AdminNavigationBar/>
|
||||
<div class={"admin-page-container"}>
|
||||
<div class={"admin-start-container"}>
|
||||
<div class={"admin-panel-page-title"}>{format!("Editar {}", match props.edit_item {
|
||||
EditItem::Agent => "Agente",
|
||||
EditItem::Project => "Proyecto",
|
||||
EditItem::Unit(_) => "Unidad",
|
||||
})}</div>
|
||||
{
|
||||
match props.edit_item {
|
||||
EditItem::Agent => {html! {}},
|
||||
EditItem::Project => {html! { <ProjectFields listing={(*listing).clone()}/> }},
|
||||
EditItem::Unit(_) => {html! {}},
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone)]
|
||||
pub enum EditItem {
|
||||
Agent,
|
||||
Project,
|
||||
/// Project Uid
|
||||
Unit(Uuid),
|
||||
}
|
||||
#[derive(PartialEq, Clone)]
|
||||
pub enum EditType {
|
||||
New, Existing(Uuid)
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct AdminEditPageProps {
|
||||
pub edit_item: EditItem,
|
||||
pub edit_type: EditType,
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq, Clone)]
|
||||
pub struct ProjectFieldsProps {
|
||||
pub listing: Option<Listing>
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[function_component(ProjectFields)]
|
||||
pub fn generate_fields_for_project(props: &ProjectFieldsProps) -> Html {
|
||||
let listing_opt = props.listing.clone();
|
||||
let location_city = use_state(|| listing_opt.clone().unwrap_or_default().location.city);
|
||||
let location_district = use_state(|| listing_opt.clone().unwrap_or_default().location.district);
|
||||
let agent: UseStateHandle<Option<Agent>> = use_state(|| {
|
||||
match listing_opt.clone() {
|
||||
Some(listing) => Some(listing.agent),
|
||||
None => None,
|
||||
}
|
||||
});
|
||||
let project_state = use_state(|| listing_opt.clone().unwrap_or_default().project.project_state);
|
||||
let project_condition = use_state(|| listing_opt.clone().unwrap_or_default().project.project_condition);
|
||||
let project_type = use_state(|| listing_opt.clone().unwrap_or_default().project.project_type);
|
||||
let project_description = use_state(|| listing_opt.clone().unwrap_or_default().project.description);
|
||||
let project_admin_tag: UseStateHandle<Option<String>> = use_state(|| listing_opt.clone().unwrap_or_default().project.admin_tag);
|
||||
let project_finish_date = use_state(|| listing_opt.clone().unwrap_or_default().project.finish_date);
|
||||
let project_floors = use_state(|| listing_opt.clone().unwrap_or_default().project.floors);
|
||||
let media: UseStateHandle<MediaList> = use_state(|| listing_opt.clone().unwrap_or_default().project.media);
|
||||
let units: UseStateHandle<Vec<Unit>> = use_state(|| listing_opt.clone().unwrap_or_default().units);
|
||||
|
||||
|
||||
html! {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for EditItem {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
EditItem::Agent => write!(f, "agent"),
|
||||
EditItem::Project => write!(f, "project"),
|
||||
EditItem::Unit(id) => write!(f, "unit{id}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Display for EditType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
EditType::New => write!(f, "new"),
|
||||
EditType::Existing(id) => {
|
||||
let fmt_id = id.to_string().replace("-", "");
|
||||
write!(f, "existing{fmt_id}")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
impl FromStr for EditItem {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if s.starts_with("unit") {
|
||||
let (_, p_id) = s.split_at(4);
|
||||
let uid: Result<Uuid, _> = p_id.try_into();
|
||||
if let Ok(id) = uid {
|
||||
return Ok(Self::Unit(id));
|
||||
}
|
||||
}
|
||||
match s {
|
||||
"agent" => Ok(Self::Agent),
|
||||
"project" => Ok(Self::Project),
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
impl FromStr for EditType {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if s.starts_with("existing") {
|
||||
let existing = s.replace("-", "");
|
||||
let (_, p_id) = existing.split_at(8);
|
||||
let uid = p_id.to_string();
|
||||
let uid: Result<Uuid, _> = uid.as_str().try_into();
|
||||
if let Ok(id) = uid {
|
||||
return Ok(Self::Existing(id));
|
||||
}
|
||||
}
|
||||
match s {
|
||||
"new" => Ok(Self::New),
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
}
|
@ -2,4 +2,5 @@ pub mod login;
|
||||
pub mod start;
|
||||
pub mod projects;
|
||||
pub mod contacts;
|
||||
pub mod agents;
|
||||
pub mod agents;
|
||||
pub mod edit;
|
@ -1,15 +1,41 @@
|
||||
use yew::prelude::*;
|
||||
|
||||
use crate::components::admin_nav_bar::AdminNavigationBar;
|
||||
use crate::{components::{admin_nav_bar::AdminNavigationBar, admin_project::AdminProject}, api::backend::get_all_projects_with_filters_paged};
|
||||
|
||||
#[function_component(AdminProjects)]
|
||||
pub fn admin_projects() -> Html {
|
||||
|
||||
let projects = use_state(|| Vec::new());
|
||||
|
||||
use_state(|| {
|
||||
let projects_handle = projects.clone();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
match get_all_projects_with_filters_paged(&1, Vec::new()).await {
|
||||
Ok(projects) => {
|
||||
projects_handle.set(projects);
|
||||
},
|
||||
Err(error) => log::error!("Error retrieving projects from backend. Error: {}", error),
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
html! {
|
||||
<>
|
||||
<AdminNavigationBar/>
|
||||
<div class={"page-container"}>
|
||||
<div class={"admin-page-container"}>
|
||||
<div class={"admin-start-container"}>
|
||||
{""}
|
||||
<div class={"admin-panel-page-title"}>{"Proyectos"}</div>
|
||||
<div class={"admin-projects-table"}>
|
||||
<div class={"admin-navbar-divider"}></div>
|
||||
{(*projects).clone().into_iter().enumerate().map(|(key, project)| html! {
|
||||
<>
|
||||
<AdminProject project={project} index={key}/>
|
||||
<div class={"admin-navbar-divider"}></div>
|
||||
</>
|
||||
}).collect::<Html>()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
@ -3,7 +3,7 @@ use yew::prelude::*;
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{pages::{landing::LandingPage, search::{SearchPage}, details::DetailsPage, not_found::NotFoundPage, contact::ContactPage, admin::{login::AdminLoginPage, start::AdminStart, projects::AdminProjects, agents::AdminAgents, contacts::AdminContacts}, agents::AgentsPage}};
|
||||
use crate::{pages::{landing::LandingPage, search::{SearchPage}, details::DetailsPage, not_found::NotFoundPage, contact::ContactPage, admin::{login::AdminLoginPage, start::AdminStart, projects::AdminProjects, agents::AdminAgents, contacts::AdminContacts, edit::{EditType, EditItem, AdminEditPage}}, agents::AgentsPage}};
|
||||
|
||||
#[derive(Clone, Routable, PartialEq)]
|
||||
pub enum Route {
|
||||
@ -28,6 +28,8 @@ pub enum Route {
|
||||
AdminAgents,
|
||||
#[at("/admin/contacts")]
|
||||
AdminContacts,
|
||||
#[at("/admin/edit/:edit_type/:edit_item")]
|
||||
AdminEdit { edit_type: EditType, edit_item: EditItem },
|
||||
|
||||
#[not_found]
|
||||
#[at("/404")]
|
||||
@ -48,7 +50,6 @@ pub fn switch(routes: Route) -> Html {
|
||||
Route::AdminProjects => html! { <AdminProjects/> },
|
||||
Route::AdminAgents => html! { <AdminAgents/> },
|
||||
Route::AdminContacts => html! { <AdminContacts/> },
|
||||
|
||||
|
||||
Route::AdminEdit { edit_type, edit_item } => html! { <AdminEditPage edit_item={edit_item} edit_type={edit_type}/> },
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user