use reinhardt_pages::component::Page;
use reinhardt_pages::form;
use reinhardt_pages::page;
pub fn login_form(error_message: Option<&str>) -> Page {
let error_html = error_message.map(|msg| {
let msg = msg.to_string();
page!(|msg: String| {
div {
class: "admin-alert admin-alert-danger mt-4 text-center text-sm",
role: "alert",
{ msg }
}
})(msg)
});
let form_page = build_login_form();
let error_page = error_html.unwrap_or_else(|| page!(|| { span {} })());
page!(|form_page: Page, error_page: Page| {
div {
class: "flex justify-center items-center min-h-screen bg-slate-50 animate__animated animate__fadeIn",
div {
class: "admin-login-card",
div {
class: "p-8",
h2 {
class: "font-display text-2xl font-bold text-center mb-1 text-slate-900",
"Admin Login"
}
p {
class: "text-sm text-slate-500 text-center mb-6",
"Sign in to manage your application"
}
{ form_page }
}
}
{ error_page }
}
})(form_page, error_page)
}
fn build_login_form() -> Page {
#[allow(unused_imports)]
use crate::server::login::admin_login_with_header;
let login_form = form! {
name: AdminLoginForm,
server_fn: admin_login_with_header,
method: Post,
redirect_on_success: "/admin/",
fields: {
username: CharField {
required,
label: "Username",
label_class: "admin-label",
wrapper_class: "mb-4",
class: "admin-input",
autocomplete: "username",
autofocus,
placeholder: "Enter your username",
}
password: CharField {
required,
widget: PasswordInput,
label: "Password",
label_class: "admin-label",
wrapper_class: "mb-5",
class: "admin-input",
autocomplete: "current-password",
placeholder: "Enter your password",
}
}
watch: {
login_error: |form| {
let error = form.error().get();
let has_error = error.is_some();
let error_message = error.unwrap_or_default();
page!(|has_error: bool, error_message: String| {
div {
id : "login-error",
class : if has_error { "admin-alert admin-alert-danger mb-4" } else { "hidden" },
role : "alert",
{ error_message }
}
})(has_error, error_message)
},
}
slots: {
after_fields: | | page!(|| {
button {
type : "submit",
class : "admin-btn admin-btn-primary w-full py-2.5 text-base",
id : "login-submit-btn",
"Sign in"
}
})(),
}
};
login_form.into_page()
}
pub fn login_view() -> Page {
login_form(None)
}
#[cfg(all(test, server))]
mod tests {
use super::*;
use rstest::rstest;
#[rstest]
fn test_login_form_renders() {
let page = login_form(None);
let html = page.render_to_string();
assert!(html.contains("Admin Login"));
assert!(html.contains("username"));
assert!(html.contains("password"));
assert!(html.contains("Sign in"));
}
#[rstest]
fn test_login_form_with_error() {
let page = login_form(Some("Invalid credentials"));
let html = page.render_to_string();
assert!(html.contains("Invalid credentials"));
assert!(html.contains("admin-alert-danger"));
}
#[rstest]
fn test_login_view_renders() {
let page = login_view();
let html = page.render_to_string();
assert!(html.contains("admin-login-form"));
}
}