Fixed spacing issues, added most client corrections and remax logo to footer, along with socials
This commit is contained in:
parent
2e4f9e201e
commit
090d285ff9
13
Readme.md
13
Readme.md
@ -7,3 +7,16 @@
|
|||||||
- [ ] Whatsapp button link
|
- [ ] Whatsapp button link
|
||||||
- [ ] Contact widget should be whatsapp OR email depending on agent contact info
|
- [ ] Contact widget should be whatsapp OR email depending on agent contact info
|
||||||
- [ ] Footer should have call to action?
|
- [ ] Footer should have call to action?
|
||||||
|
|
||||||
|
- [x] If no properties are found footer jumps up, shouldn't happen.
|
||||||
|
- [x] Agentes / Quienes Somos (Similar al de remax) (recordar poner a jorge ledesma de primero y despues a los demas agentes)
|
||||||
|
- [x] Que se pueda buscar por cant. de habitaciones
|
||||||
|
- [x] Cambiar unidad 1 unid 2 etc... -> Tipo A Tipo B Tipo C Etc...
|
||||||
|
- [x] Cambiar orden de el menu de unidades (poner area comun de ultimo) y quitar que se pueda varias areas comunes
|
||||||
|
- [x] Spacing de menu de unidades
|
||||||
|
- [x] Poner Features arriba de la desc y las imagenes de la unidad
|
||||||
|
- [ ] Poner el logo de remax en el footer
|
||||||
|
- [ ] Poner logos de facebook, instagram, youtube, en el footer (Sin enlaces)
|
||||||
|
- [ ] Poner boton de solicita tu prestamo, que rediriga a remax (Se te dará el enlace), abajo de el disclaimer en la página de detalles de proyecto
|
||||||
|
- [ ] Contenido statico folder de imagenes para la imagen de portada de inicio, que se roten aleatoriamente cada día
|
||||||
|
- [x] Space between Unit selection menu and unit media on empty unit features
|
0
css/admin-login.css
Normal file
0
css/admin-login.css
Normal file
@ -2,11 +2,11 @@
|
|||||||
.footer {
|
.footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: space-evenly;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 170px;
|
height: 210px;
|
||||||
|
|
||||||
background-color: #02114A;
|
background-color: #02114A;
|
||||||
}
|
}
|
||||||
@ -19,11 +19,28 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
}
|
}
|
||||||
|
.footer-remax-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
.footer-logo {
|
.footer-logo {
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
}
|
}
|
||||||
|
.footer-remax-logo {
|
||||||
|
position: absolute;
|
||||||
|
margin-left: 140px;
|
||||||
|
margin-top: -15px;
|
||||||
|
object-fit: cover;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
.footer-copyright-text {
|
.footer-copyright-text {
|
||||||
font-size: 9px;
|
font-size: 9px;
|
||||||
font-family: Inter;
|
font-family: Inter;
|
||||||
@ -36,6 +53,7 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 3px;
|
gap: 3px;
|
||||||
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
.footer-credit {
|
.footer-credit {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
@ -54,6 +72,26 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition-duration: 0.4s;
|
transition-duration: 0.4s;
|
||||||
}
|
}
|
||||||
|
.footer-brand-socials {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.footer-brand-social-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
border-radius: 30px;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
.footer-call-to-action-container {
|
.footer-call-to-action-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -81,3 +81,16 @@
|
|||||||
from {opacity: .4}
|
from {opacity: .4}
|
||||||
to {opacity: 1}
|
to {opacity: 1}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Desktop view */
|
||||||
|
@media only screen and (min-width: 750px) {
|
||||||
|
.media-slideshow-frame {
|
||||||
|
height: 55vw;
|
||||||
|
max-height: 550px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Desktop view */
|
||||||
|
@media only screen and (min-width: 1200px) {
|
||||||
|
|
||||||
|
}
|
@ -7,7 +7,7 @@ Divide the Details page into 3 main sections:
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: stretch;
|
justify-content: stretch;
|
||||||
align-items: start;
|
align-items: center;
|
||||||
padding: 0px 15px;
|
padding: 0px 15px;
|
||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
}
|
}
|
||||||
@ -18,6 +18,7 @@ Divide the Details page into 3 main sections:
|
|||||||
justify-content: stretch;
|
justify-content: stretch;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
max-width: 1000px;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
@ -70,9 +71,10 @@ Divide the Details page into 3 main sections:
|
|||||||
.details-body {
|
.details-body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: stretch;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
max-width: 1000px;
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
margin-top: -10px;
|
margin-top: -10px;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
@ -95,14 +97,15 @@ Divide the Details page into 3 main sections:
|
|||||||
|
|
||||||
.details-body-features {
|
.details-body-features {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-flow: row wrap;
|
||||||
justify-content: stretch;
|
flex-direction: row;
|
||||||
align-items: start;
|
justify-content: center;
|
||||||
gap: 25px;
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 3%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
margin-bottom: 60px;
|
margin: 20px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.details-body-feature-item {
|
.details-body-feature-item {
|
||||||
@ -114,6 +117,7 @@ Divide the Details page into 3 main sections:
|
|||||||
|
|
||||||
font-family: Inter;
|
font-family: Inter;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
|
width: clamp(200px, calc(100% * (0.40) - 10px), 1000px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.details-body-feature-item-icon-container {
|
.details-body-feature-item-icon-container {
|
||||||
@ -162,10 +166,9 @@ Divide the Details page into 3 main sections:
|
|||||||
.details-body-units-selection-container {
|
.details-body-units-selection-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: space-evenly;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 30px;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,20 +217,26 @@ Divide the Details page into 3 main sections:
|
|||||||
color: rgba(34, 34, 34, 0.873);
|
color: rgba(34, 34, 34, 0.873);
|
||||||
}
|
}
|
||||||
|
|
||||||
.details-body-unit-features {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: stretch;
|
|
||||||
align-items: start;
|
|
||||||
gap: 25px;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 200px;
|
|
||||||
margin-bottom: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.details-body-disclaimer {
|
.details-body-disclaimer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-family: Inter;
|
font-family: Inter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Desktop view */
|
||||||
|
@media only screen and (min-width: 750px) {
|
||||||
|
.details-body-units-media-slideshow-frame {
|
||||||
|
height: 55vw;
|
||||||
|
max-height: 550px;
|
||||||
|
}
|
||||||
|
.details-body-features {
|
||||||
|
flex-direction: row;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Desktop view */
|
||||||
|
@media only screen and (min-width: 1200px) {
|
||||||
|
|
||||||
|
}
|
@ -117,6 +117,9 @@
|
|||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
font-family: Space Grotesk;
|
font-family: Space Grotesk;
|
||||||
}
|
}
|
||||||
|
.navbar-item-selected:hover {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
.navbar-item:hover {
|
.navbar-item:hover {
|
||||||
background-color: #ffffff14;
|
background-color: #ffffff14;
|
||||||
font-size: 13pt;
|
font-size: 13pt;
|
||||||
@ -207,6 +210,7 @@
|
|||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
font-family: Source Sans Pro;
|
font-family: Source Sans Pro;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-item:hover {
|
.navbar-item:hover {
|
||||||
|
@ -92,7 +92,7 @@
|
|||||||
.project-search-button {
|
.project-search-button {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
background-color: #5D6A73;
|
background-color: #02114A;
|
||||||
color: white;
|
color: white;
|
||||||
border: 0px;
|
border: 0px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
@ -105,7 +105,6 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
transition-duration: 0.3s;
|
transition-duration: 0.3s;
|
||||||
background-color: #252631;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Results */
|
/* Results */
|
||||||
|
@ -10,12 +10,15 @@
|
|||||||
<link data-trunk type="text/css" href="css/search.css" rel="css" />
|
<link data-trunk type="text/css" href="css/search.css" rel="css" />
|
||||||
<link data-trunk type="text/css" href="css/project_card.css" rel="css" />
|
<link data-trunk type="text/css" href="css/project_card.css" rel="css" />
|
||||||
<link data-trunk type="text/css" href="css/details.css" rel="css" />
|
<link data-trunk type="text/css" href="css/details.css" rel="css" />
|
||||||
|
<link data-trunk type="text/css" href="css/admin-login.css" rel="css" />
|
||||||
|
|
||||||
<link data-trunk type="text/css" href="css/components/loading.css" rel="css" />
|
<link data-trunk type="text/css" href="css/components/loading.css" rel="css" />
|
||||||
<link data-trunk type="text/css" href="css/components/media_slideshow.css" rel="css" />
|
<link data-trunk type="text/css" href="css/components/media_slideshow.css" rel="css" />
|
||||||
<link data-trunk type="text/css" href="css/components/floating_widget.css" rel="css" />
|
<link data-trunk type="text/css" href="css/components/floating_widget.css" rel="css" />
|
||||||
<link data-trunk type="text/css" href="css/components/footer.css" rel="css" />
|
<link data-trunk type="text/css" href="css/components/footer.css" rel="css" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
|
||||||
<link href="https://fonts.googleapis.com/css?family=Space+Grotesk" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css?family=Space+Grotesk" rel="stylesheet">
|
||||||
<link href="https://fonts.googleapis.com/css?family=Inter" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css?family=Inter" rel="stylesheet">
|
||||||
|
@ -5,7 +5,21 @@ pub fn page_footer() -> Html {
|
|||||||
html! {
|
html! {
|
||||||
<div class={"footer"}>
|
<div class={"footer"}>
|
||||||
<div class={"footer-brand-container"}>
|
<div class={"footer-brand-container"}>
|
||||||
|
<div class={"footer-remax-container"}>
|
||||||
<img class={"footer-logo"} src={"images/logo-white.png"}/>
|
<img class={"footer-logo"} src={"images/logo-white.png"}/>
|
||||||
|
<img class={"footer-remax-logo"} src={"https://remaxrd.com/icons/ballow.svg"}/>
|
||||||
|
</div>
|
||||||
|
<div class={"footer-brand-socials"}>
|
||||||
|
<div class={"footer-brand-social-container"}>
|
||||||
|
<i class="fa-brands fa-instagram"></i>
|
||||||
|
</div>
|
||||||
|
<div class={"footer-brand-social-container"}>
|
||||||
|
<i class="fa-brands fa-youtube"></i>
|
||||||
|
</div>
|
||||||
|
<div class={"footer-brand-social-container"}>
|
||||||
|
<i class="fa-brands fa-facebook"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class={"footer-copyright-text"}>{"© 2023 Proyectos en Construcción - Todos los derechos Reservados"}</div>
|
<div class={"footer-copyright-text"}>{"© 2023 Proyectos en Construcción - Todos los derechos Reservados"}</div>
|
||||||
<div class={"footer-credit-container"}>
|
<div class={"footer-credit-container"}>
|
||||||
<div class={"footer-credit"}>{"Powered by"}</div>
|
<div class={"footer-credit"}>{"Powered by"}</div>
|
||||||
@ -13,6 +27,10 @@ pub fn page_footer() -> Html {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
<div class={"footer-call-to-action-container"}>
|
<div class={"footer-call-to-action-container"}>
|
||||||
<input placeholder={"AAA"} class={"footer-call-to-action-input"}/>
|
<input placeholder={"AAA"} class={"footer-call-to-action-input"}/>
|
||||||
|
@ -2,7 +2,7 @@ use stdweb::web::{IEventTarget, event::ResizeEvent};
|
|||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yew_router::prelude::{use_navigator, use_route};
|
use yew_router::prelude::{use_navigator, use_route};
|
||||||
|
|
||||||
use crate::{routes::main_router::{Route}, constants::{NAVBAR_COL_LANDING, NAVBAR_COL_CONTACTO, NAVBAR_COL_PROYECTOS}};
|
use crate::{routes::main_router::{Route}, constants::{NAVBAR_COL_LANDING, NAVBAR_COL_CONTACTO, NAVBAR_COL_PROYECTOS, NAVBAR_COL_AGENTES}};
|
||||||
//use yew_router::prelude::use_navigator;
|
//use yew_router::prelude::use_navigator;
|
||||||
|
|
||||||
|
|
||||||
@ -16,6 +16,7 @@ pub fn navigation_bar() -> Html {
|
|||||||
let cloned_navigator_3 = navigator.clone();
|
let cloned_navigator_3 = navigator.clone();
|
||||||
let cloned_navigator_4 = navigator.clone();
|
let cloned_navigator_4 = navigator.clone();
|
||||||
let cloned_navigator_5 = navigator.clone();
|
let cloned_navigator_5 = navigator.clone();
|
||||||
|
let cloned_navigator_6 = navigator.clone();
|
||||||
|
|
||||||
let navbar_toggle = use_state(|| true);
|
let navbar_toggle = use_state(|| true);
|
||||||
let on_click_hamburger = {
|
let on_click_hamburger = {
|
||||||
@ -54,10 +55,17 @@ pub fn navigation_bar() -> Html {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div onclick={move |_| cloned_navigator_2.push(&Route::Search)} class={
|
<div onclick={move |_| cloned_navigator_2.push(&Route::Search)} class={
|
||||||
if current_route.is_some() && matches!(current_route.unwrap(), Route::Search) {"navbar-item-selected"} else {"navbar-item"}
|
if current_route.is_some() && matches!(current_route.clone().unwrap(), Route::Search) {"navbar-item-selected"} else {"navbar-item"}
|
||||||
}>
|
}>
|
||||||
{NAVBAR_COL_PROYECTOS}
|
{NAVBAR_COL_PROYECTOS}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div onclick={move |_| cloned_navigator_6.push(&Route::Agentes)} class={
|
||||||
|
if current_route.is_some() && matches!(current_route.unwrap(), Route::Agentes) {"navbar-item-selected"} else {"navbar-item"}
|
||||||
|
}>
|
||||||
|
{NAVBAR_COL_AGENTES}
|
||||||
|
</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
if (*window_device_handle) == WindowDevice::Mobile {
|
if (*window_device_handle) == WindowDevice::Mobile {
|
||||||
html! {
|
html! {
|
||||||
|
@ -27,7 +27,7 @@ pub fn project_card(props: &ProjectCardProps) -> Html {
|
|||||||
|
|
||||||
let project_title = format!("{} en {}, {}", props.project.project_type, props.project.district, props.project.city);
|
let project_title = format!("{} en {}, {}", props.project.project_type, props.project.district, props.project.city);
|
||||||
|
|
||||||
let project_price = format!("Desde ${} USD", match props.project.starts_from {
|
let project_price = format!("Desde US${}", match props.project.starts_from {
|
||||||
Some(price) => {
|
Some(price) => {
|
||||||
let price_separated = price.separate_with_commas();
|
let price_separated = price.separate_with_commas();
|
||||||
if price_separated.contains(".") {
|
if price_separated.contains(".") {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
pub const NAVBAR_COL_LANDING: &str = "Inicio";
|
pub const NAVBAR_COL_LANDING: &str = "Inicio";
|
||||||
pub const NAVBAR_COL_PROYECTOS: &str = "Proyectos";
|
pub const NAVBAR_COL_PROYECTOS: &str = "Proyectos";
|
||||||
pub const NAVBAR_COL_CONTACTO: &str = "Contacto";
|
pub const NAVBAR_COL_CONTACTO: &str = "Contacto";
|
||||||
|
pub const NAVBAR_COL_AGENTES: &str = "Agentes";
|
||||||
|
|
||||||
pub const PRIMARY_COLOR: &str = "#41BDD9";
|
pub const PRIMARY_COLOR: &str = "#41BDD9";
|
||||||
pub const SECONDARY_COLOR: &str = "#5D6A73";
|
pub const SECONDARY_COLOR: &str = "#5D6A73";
|
19
src/pages/admin/login.rs
Normal file
19
src/pages/admin/login.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
use yew::prelude::*;
|
||||||
|
use yew_router::prelude::use_navigator;
|
||||||
|
|
||||||
|
#[function_component(AdminLoginPage)]
|
||||||
|
pub fn login_page() -> Html {
|
||||||
|
let _navigator = use_navigator().unwrap();
|
||||||
|
html! {
|
||||||
|
<div class={"admin-login-container"}>
|
||||||
|
<div class={"admin-login-title"}>
|
||||||
|
{"Portal de Administración"}
|
||||||
|
</div>
|
||||||
|
<div class={"admin-login-form-container"}>
|
||||||
|
<div class={"admin-login-form-textfield-"}>
|
||||||
|
{"Google"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
1
src/pages/admin/mod.rs
Normal file
1
src/pages/admin/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod login;
|
17
src/pages/agents.rs
Normal file
17
src/pages/agents.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use yew::prelude::*;
|
||||||
|
|
||||||
|
use crate::components::{nav_bar::NavigationBar, footer::PageFooter};
|
||||||
|
|
||||||
|
|
||||||
|
#[function_component(AgentsPage)]
|
||||||
|
pub fn agents_page() -> Html {
|
||||||
|
html!{
|
||||||
|
<>
|
||||||
|
<NavigationBar/>
|
||||||
|
<div class={"page-container"} style={"margin-bottom: 72vh"}> //TODO: remove this margin when landing page done
|
||||||
|
{"Agents Page"}
|
||||||
|
</div>
|
||||||
|
<PageFooter/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
use jl_types::{dto::listing::Listing, domain::unit_type::UnitType};
|
use jl_types::{dto::listing::Listing, domain::{unit_type::UnitType, unit::Unit}};
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use thousands::Separable;
|
use thousands::Separable;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@ -6,6 +6,8 @@ use yew::prelude::*;
|
|||||||
|
|
||||||
use crate::{components::{nav_bar::NavigationBar, media_slideshow::MediaSlideshow, floating_widget::FloatingWidget, feature::FeatureItem, footer::PageFooter}, api::backend::get_project_listing};
|
use crate::{components::{nav_bar::NavigationBar, media_slideshow::MediaSlideshow, floating_widget::FloatingWidget, feature::FeatureItem, footer::PageFooter}, api::backend::get_project_listing};
|
||||||
|
|
||||||
|
const ALPHABET_LETTERS: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
|
|
||||||
#[function_component(DetailsPage)]
|
#[function_component(DetailsPage)]
|
||||||
pub fn details_page(props: &DetailsPageProps) -> Html {
|
pub fn details_page(props: &DetailsPageProps) -> Html {
|
||||||
let finished_loading = use_state(|| false);
|
let finished_loading = use_state(|| false);
|
||||||
@ -30,6 +32,12 @@ pub fn details_page(props: &DetailsPageProps) -> Html {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let listing = (*listing_handle).clone();
|
let listing = (*listing_handle).clone();
|
||||||
|
let mut forsale_units: Vec<&Unit> = listing.units.iter().filter(|unit| unit.unit_type == UnitType::ForSale).collect();
|
||||||
|
let mut not_forsale_units: Vec<&Unit> = listing.units.iter().filter(|unit| unit.unit_type == UnitType::NotForSale).collect();
|
||||||
|
|
||||||
|
let mut organized_units = Vec::new();
|
||||||
|
organized_units.append(&mut forsale_units);
|
||||||
|
organized_units.append(&mut not_forsale_units);
|
||||||
|
|
||||||
let project_title = format!("{} en {}, {}", &listing.project.project_type, &listing.location.district, &listing.location.city);
|
let project_title = format!("{} en {}, {}", &listing.project.project_type, &listing.location.district, &listing.location.city);
|
||||||
//let project_description = &listing.project.description;
|
//let project_description = &listing.project.description;
|
||||||
@ -43,7 +51,7 @@ pub fn details_page(props: &DetailsPageProps) -> Html {
|
|||||||
|
|
||||||
let cloned_selected_unit_handle = selected_unit_handle.clone();
|
let cloned_selected_unit_handle = selected_unit_handle.clone();
|
||||||
|
|
||||||
let selected_unit_opt = listing.units.get(*cloned_selected_unit_handle);
|
let selected_unit_opt = organized_units.get(*cloned_selected_unit_handle);
|
||||||
html!{
|
html!{
|
||||||
<>
|
<>
|
||||||
<NavigationBar/>
|
<NavigationBar/>
|
||||||
@ -68,10 +76,10 @@ pub fn details_page(props: &DetailsPageProps) -> Html {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class={"details-body-divider"}></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class={"details-body-divider"}></div>
|
|
||||||
|
|
||||||
<div class={"details-body"}>
|
<div class={"details-body"}>
|
||||||
<div class={"details-body-description"}>
|
<div class={"details-body-description"}>
|
||||||
@ -109,43 +117,39 @@ pub fn details_page(props: &DetailsPageProps) -> Html {
|
|||||||
html! {
|
html! {
|
||||||
<div class={"details-body-units"}>
|
<div class={"details-body-units"}>
|
||||||
<div class={"details-body-units-title"}>{"Unidades"}</div>
|
<div class={"details-body-units-title"}>{"Unidades"}</div>
|
||||||
<div class={"details-body-units-selection-container"}>
|
|
||||||
{ // Maps all the units to generate the unit selection menu
|
{create_unit_selection_menu(&organized_units, selected_unit_handle)}
|
||||||
listing.units.iter().enumerate().map(|(index, unit)| {
|
|
||||||
let cloned_selected_unit_handle = cloned_selected_unit_handle.clone();
|
|
||||||
let select_unit_onclick_cb = {
|
|
||||||
let cloned_selected_unit_handle = cloned_selected_unit_handle.clone();
|
|
||||||
Callback::from(move |_| cloned_selected_unit_handle.set(index))
|
|
||||||
};
|
|
||||||
html! {
|
|
||||||
<div class={
|
|
||||||
if *cloned_selected_unit_handle == index
|
|
||||||
{"details-body-units-selection-item-selected"}
|
|
||||||
else
|
|
||||||
{"details-body-units-selection-item-unselected"}
|
|
||||||
} onclick={select_unit_onclick_cb}>
|
|
||||||
{
|
{
|
||||||
match unit.unit_type {
|
if unit.unit_type == UnitType::ForSale {
|
||||||
UnitType::ForSale => html! {
|
let price_separated = unit.price_usd.separate_with_commas();
|
||||||
|
html! {
|
||||||
|
<div class={"details-body-features"}>
|
||||||
|
{
|
||||||
|
html! {
|
||||||
<>
|
<>
|
||||||
<i class="fa-solid fa-house-circle-check"></i>
|
<FeatureItem title={"Precio"} icon={"fa-solid fa-money-bill-trend-up"} subtitle={
|
||||||
<div>{format!("Unidad {}", index + 1)}</div>
|
if price_separated.contains(".") { //TODO: Desde en el precio
|
||||||
</>
|
format!("Desde US${}", price_separated)
|
||||||
},
|
} else {
|
||||||
UnitType::NotForSale => html! {
|
format!("Desde US${}", format!("{price_separated}.00"))
|
||||||
<>
|
}
|
||||||
<i class="fa-solid fa-people-group"></i>
|
}/>
|
||||||
<div>{"Área común"}</div>
|
<FeatureItem title={"Habitaciones"} icon={"fa-solid fa-bed"} subtitle={format!("{}", unit.rooms)}/>
|
||||||
|
<FeatureItem title={"Baños"} icon={"fa-solid fa-bath"} subtitle={format!("{}", unit.bathrooms)}/>
|
||||||
|
<FeatureItem title={"Metraje"} icon={"fa-solid fa-ruler-combined"} subtitle={format!("{}m²", unit.area)}/>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
html! {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}).collect::<Html>()
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<div class={"details-body-units-media-slideshow-frame"}>
|
<div class={"details-body-units-media-slideshow-frame"}>
|
||||||
{
|
{
|
||||||
if unit.media.media_list.len() >= 1 {
|
if unit.media.media_list.len() >= 1 {
|
||||||
@ -172,32 +176,7 @@ pub fn details_page(props: &DetailsPageProps) -> Html {
|
|||||||
|
|
||||||
<div class={"details-body-divider"}></div>
|
<div class={"details-body-divider"}></div>
|
||||||
|
|
||||||
<div class={"details-body-unit-features"}>
|
|
||||||
|
|
||||||
{ // Features only to be shown on a ForSale unit
|
|
||||||
if unit.unit_type == UnitType::ForSale {
|
|
||||||
let price_separated = unit.price_usd.separate_with_commas();
|
|
||||||
html! {
|
|
||||||
<>
|
|
||||||
<FeatureItem title={"Inversión"} icon={"fa-solid fa-money-bill-trend-up"} subtitle={
|
|
||||||
if price_separated.contains(".") {
|
|
||||||
format!("${} USD", price_separated)
|
|
||||||
} else {
|
|
||||||
format!("${} USD", format!("{price_separated}.00"))
|
|
||||||
}
|
|
||||||
}/>
|
|
||||||
<FeatureItem title={"Habitaciones"} icon={"fa-solid fa-bed"} subtitle={format!("{}", unit.rooms)}/>
|
|
||||||
<FeatureItem title={"Baños"} icon={"fa-solid fa-bath"} subtitle={format!("{}", unit.bathrooms)}/>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
} else {html! {}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Features that always are shown
|
|
||||||
<FeatureItem title={"Metraje"} icon={"fa-solid fa-ruler-combined"} subtitle={format!("{}m²", unit.area)}/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class={"details-body-divider"}></div>
|
|
||||||
|
|
||||||
<div class={"details-body-disclaimer"}>
|
<div class={"details-body-disclaimer"}>
|
||||||
{"*Unidades sujetas a disponibilidad"}
|
{"*Unidades sujetas a disponibilidad"}
|
||||||
@ -244,3 +223,47 @@ pub struct DetailsPageProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn create_unit_selection_menu(units: &Vec<&Unit>, selected_unit_handle: UseStateHandle<usize>) -> Html {
|
||||||
|
|
||||||
|
html! {
|
||||||
|
<div class={"details-body-units-selection-container"}>
|
||||||
|
{ // Maps all the units to generate the unit selection menu
|
||||||
|
units.iter().enumerate().map(|(index, unit)| {
|
||||||
|
let cloned_selected_unit_handle = selected_unit_handle.clone();
|
||||||
|
let select_unit_onclick_cb = {
|
||||||
|
let cloned_selected_unit_handle = cloned_selected_unit_handle.clone();
|
||||||
|
Callback::from(move |_| cloned_selected_unit_handle.set(index))
|
||||||
|
};
|
||||||
|
html! {
|
||||||
|
<div class={
|
||||||
|
if *cloned_selected_unit_handle == index
|
||||||
|
{"details-body-units-selection-item-selected"}
|
||||||
|
else
|
||||||
|
{"details-body-units-selection-item-unselected"}
|
||||||
|
} onclick={select_unit_onclick_cb}>
|
||||||
|
{
|
||||||
|
match unit.unit_type {
|
||||||
|
UnitType::ForSale => html! {
|
||||||
|
<>
|
||||||
|
<i class="fa-solid fa-house-circle-check"></i>
|
||||||
|
<div>{format!("Unidad {}", ALPHABET_LETTERS.chars().nth(index).unwrap_or_else(|| index.to_string().chars().nth(1).unwrap()))}</div>
|
||||||
|
</>
|
||||||
|
},
|
||||||
|
UnitType::NotForSale => html! {
|
||||||
|
<>
|
||||||
|
<i class="fa-solid fa-people-group"></i>
|
||||||
|
<div>{"Área común"}</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}).collect::<Html>()
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
@ -3,3 +3,5 @@ pub mod search;
|
|||||||
pub mod details;
|
pub mod details;
|
||||||
pub mod not_found;
|
pub mod not_found;
|
||||||
pub mod contact;
|
pub mod contact;
|
||||||
|
pub mod admin;
|
||||||
|
pub mod agents;
|
@ -54,6 +54,7 @@ pub fn search_page() -> Html {
|
|||||||
let project_city_filter: UseStateHandle<OptionWrapper<String>> = use_state(|| OptionWrapper::new(None));
|
let project_city_filter: UseStateHandle<OptionWrapper<String>> = use_state(|| OptionWrapper::new(None));
|
||||||
// Dropdown
|
// Dropdown
|
||||||
let project_district_filter: UseStateHandle<OptionWrapper<String>> = use_state(|| OptionWrapper::new(None));
|
let project_district_filter: UseStateHandle<OptionWrapper<String>> = use_state(|| OptionWrapper::new(None));
|
||||||
|
let unit_rooms_filter: UseStateHandle<OptionWrapper<usize>> = use_state(|| OptionWrapper::new(None));
|
||||||
//TODO: Think about price filtering
|
//TODO: Think about price filtering
|
||||||
/*// TextField
|
/*// TextField
|
||||||
let _project_min_price_filter: UseStateHandle<OptionWrapper<f64>> = use_state(|| OptionWrapper::new(None));
|
let _project_min_price_filter: UseStateHandle<OptionWrapper<f64>> = use_state(|| OptionWrapper::new(None));
|
||||||
@ -111,7 +112,6 @@ pub fn search_page() -> Html {
|
|||||||
|
|
||||||
Callback::from(move |project_city: OptionWrapper<String>| {
|
Callback::from(move |project_city: OptionWrapper<String>| {
|
||||||
let districts_handle = districts_handle.clone();
|
let districts_handle = districts_handle.clone();
|
||||||
info!("{}", project_city.to_string());
|
|
||||||
cloned_project_city_filter.set(project_city.clone());
|
cloned_project_city_filter.set(project_city.clone());
|
||||||
wasm_bindgen_futures::spawn_local(async move {
|
wasm_bindgen_futures::spawn_local(async move {
|
||||||
match get_all_districts_in_city(&project_city.to_string()).await {
|
match get_all_districts_in_city(&project_city.to_string()).await {
|
||||||
@ -134,13 +134,24 @@ pub fn search_page() -> Html {
|
|||||||
selection_changed: {
|
selection_changed: {
|
||||||
let cloned_project_district_filter = project_district_filter.clone();
|
let cloned_project_district_filter = project_district_filter.clone();
|
||||||
Callback::from(move |project_district: OptionWrapper<String>| {
|
Callback::from(move |project_district: OptionWrapper<String>| {
|
||||||
info!("{}", project_district.to_string());
|
|
||||||
cloned_project_district_filter.set(project_district)
|
cloned_project_district_filter.set(project_district)
|
||||||
}
|
}
|
||||||
)},
|
)},
|
||||||
class_css: Some("project-search-filter-item".into())
|
class_css: Some("project-search-filter-item".into())
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let unit_room_amount_drop_down = comp_with::<DropDown<OptionWrapper<usize>>>(DropDownProps {
|
||||||
|
initial: OptionWrapper::new(None),
|
||||||
|
options: vec![OptionWrapper::new(None), OptionWrapper::new(Some(1)), OptionWrapper::new(Some(2)), OptionWrapper::new(Some(3)), OptionWrapper::new(Some(4)), OptionWrapper::new(Some(5)), OptionWrapper::new(Some(6)), OptionWrapper::new(Some(7)), OptionWrapper::new(Some(8)), OptionWrapper::new(Some(9)), OptionWrapper::new(Some(10))],
|
||||||
|
selection_changed: {
|
||||||
|
let unit_rooms_filter = unit_rooms_filter.clone();
|
||||||
|
Callback::from(move |unit_room_amount: OptionWrapper<usize>| {
|
||||||
|
unit_rooms_filter.set(unit_room_amount)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
class_css: Some("project-search-filter-item".into())
|
||||||
|
});
|
||||||
|
|
||||||
let search_onclick = {
|
let search_onclick = {
|
||||||
let search_results_handle = search_results_handle.clone();
|
let search_results_handle = search_results_handle.clone();
|
||||||
let page_counter = page_counter.clone();
|
let page_counter = page_counter.clone();
|
||||||
@ -262,6 +273,13 @@ pub fn search_page() -> Html {
|
|||||||
{project_district_drop_down}
|
{project_district_drop_down}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class={"project-search-filter-container"}>
|
||||||
|
<div class={"project-search-filter-label"}>
|
||||||
|
{"Habitaciones"}
|
||||||
|
</div>
|
||||||
|
{unit_room_amount_drop_down}
|
||||||
|
</div>
|
||||||
|
|
||||||
<button class={"project-search-button"} onclick={search_onclick}>
|
<button class={"project-search-button"} onclick={search_onclick}>
|
||||||
{"Buscar"}
|
{"Buscar"}
|
||||||
</button>
|
</button>
|
||||||
@ -273,6 +291,15 @@ pub fn search_page() -> Html {
|
|||||||
if *finished_loading {
|
if *finished_loading {
|
||||||
html!{
|
html!{
|
||||||
<div class={"project-search-results-container"}> // Search Results Content
|
<div class={"project-search-results-container"}> // Search Results Content
|
||||||
|
{if (*search_results_handle).len() == 0 {
|
||||||
|
html!{
|
||||||
|
<div style={"margin-bottom: 40vh"}>
|
||||||
|
{"No se han encontrado proyectos con los filtros de busqueda especificados."}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
html! {}
|
||||||
|
}}
|
||||||
{(*search_results_handle).clone().into_iter().map(|project| html!{<ProjectCard {project}/>}).collect::<Html>()}
|
{(*search_results_handle).clone().into_iter().map(|project| html!{<ProjectCard {project}/>}).collect::<Html>()}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use yew::prelude::*;
|
|||||||
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{pages::{landing::LandingPage, search::{SearchPage}, details::DetailsPage, not_found::NotFoundPage, contact::ContactPage}};
|
use crate::{pages::{landing::LandingPage, search::{SearchPage}, details::DetailsPage, not_found::NotFoundPage, contact::ContactPage, admin::login::AdminLoginPage, agents::AgentsPage}};
|
||||||
|
|
||||||
#[derive(Clone, Routable, PartialEq)]
|
#[derive(Clone, Routable, PartialEq)]
|
||||||
pub enum Route {
|
pub enum Route {
|
||||||
@ -15,6 +15,11 @@ pub enum Route {
|
|||||||
Details { project_id: Uuid },
|
Details { project_id: Uuid },
|
||||||
#[at("/contact")]
|
#[at("/contact")]
|
||||||
Contact,
|
Contact,
|
||||||
|
#[at("/agentes")]
|
||||||
|
Agentes,
|
||||||
|
|
||||||
|
#[at("/admin")]
|
||||||
|
Admin,
|
||||||
|
|
||||||
#[not_found]
|
#[not_found]
|
||||||
#[at("/404")]
|
#[at("/404")]
|
||||||
@ -27,6 +32,8 @@ pub fn switch(routes: Route) -> Html {
|
|||||||
Route::Search => html! { <SearchPage/> },
|
Route::Search => html! { <SearchPage/> },
|
||||||
Route::Details { project_id } => html! { <DetailsPage project_id={project_id}/> },
|
Route::Details { project_id } => html! { <DetailsPage project_id={project_id}/> },
|
||||||
Route::NotFound => html! { <NotFoundPage/> },
|
Route::NotFound => html! { <NotFoundPage/> },
|
||||||
Route::Contact => html! { <ContactPage/> }
|
Route::Contact => html! { <ContactPage/> },
|
||||||
|
Route::Admin => html! { <AdminLoginPage/> },
|
||||||
|
Route::Agentes => html! { <AgentsPage/> },
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user