pagetop/extensions/pagetop-user/src/component/login_form.rs
Manuel Cillero 1be3d88888 ♻️ Migra #[builder_fn] a #[builder_impl]
Sustituye las ~400 anotaciones `#[builder_fn]` método a método por un
único `#[builder_impl]` por `impl`/`trait`, en 74 ficheros de pagetop y
sus extensiones (admin, bootsier, menu, user).
2026-08-29 19:25:58 +02:00

111 lines
3.5 KiB
Rust

//! Componente de formulario de inicio de sesión.
use pagetop::prelude::*;
use crate::component::error_banner;
use crate::config::SETTINGS;
use crate::{LOCALES_USER, LOGIN_PATH, PASSWORD_RESET_PATH, REGISTER_PATH};
#[derive(AutoDefault, Clone, Debug, Getters)]
pub struct LoginForm {
error: Option<Lc>,
/// Página a la que volver tras iniciar sesión.
waypoint: Waypoint,
}
#[async_trait]
impl Component for LoginForm {
fn new() -> Self {
Self::default()
}
async fn prepare(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
let action = self.waypoint().append_to(cx.route(LOGIN_PATH));
let strict = SETTINGS.login_strict;
// En modo estricto se dificulta que el navegador recuerde o autorrellene las
// credenciales: nombres de campo neutros y campos `Field::strict_text()`/
// `Field::strict_password()`, que ya incluyen el resto de medidas (ver su documentación).
let username = if strict {
form::input::Field::strict_text()
} else {
form::input::Field::text().with_autocomplete(Some(form::Autocomplete::username()))
}
.with_name(if strict { "ident" } else { "username" })
.with_label(Lc::t("field-username", &LOCALES_USER))
.with_autofocus(true)
.with_required(true);
let password = if strict {
form::input::Field::strict_password()
} else {
form::input::Field::password()
.with_autocomplete(Some(form::Autocomplete::current_password()))
}
.with_name(if strict { "token" } else { "password" })
.with_label(Lc::t("field-password", &LOCALES_USER))
.with_required(true);
let mut form = Form::new()
.with_id("user-login-form")
.with_action(action)
.with_method(form::Method::Post)
.with_child(error_banner(self.error().cloned()))
.with_child(username)
.with_child(password)
.with_child(
form::Checkbox::check()
.with_name("remember")
.with_label(Lc::t("field-remember-me", &LOCALES_USER)),
)
.with_child(Button::submit(Lc::t("btn-login", &LOCALES_USER)))
.with_child(links(SETTINGS.allow_registration));
if strict {
form = form
.with_prop(PropsOp::set("autocomplete", "off"))
.with_prop(PropsOp::set("novalidate", "novalidate"));
}
let form = form.render(cx).await;
Ok(html! {
div.user-login-page {
div.user-login-card {
(form)
}
}
})
}
}
fn links(allow_registration: bool) -> Html {
Html::with(move |cx| {
html! {
@if allow_registration {
p.user-register-link {
a href=(cx.route(REGISTER_PATH)) {
(Lc::t("link-register", &LOCALES_USER).using(cx))
}
}
}
p.user-reset-link {
a href=(cx.route(PASSWORD_RESET_PATH)) {
(Lc::t("link-forgot-password", &LOCALES_USER).using(cx))
}
}
}
})
}
#[builder_impl]
impl LoginForm {
pub fn with_error(mut self, error: impl Into<Option<Lc>>) -> Self {
self.error = error.into();
self
}
pub fn with_waypoint(mut self, waypoint: impl Into<Waypoint>) -> Self {
self.waypoint = waypoint.into();
self
}
}