diff --git a/Cargo.toml b/Cargo.toml index a69a1e8..416cea4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ cfg-if = "1.0.0" chrono = { version = "0.4.38", features = ["serde", "wasm-bindgen"] } axum-login = "0.15.3" password-auth = "1.0.0" +lazy_static = "1.5" [target.'cfg(engine)'.dev-dependencies] fantoccini = "0.19" diff --git a/src/capsules/login_form.rs b/src/capsules/login_form.rs new file mode 100644 index 0000000..7849a39 --- /dev/null +++ b/src/capsules/login_form.rs @@ -0,0 +1,86 @@ +use lazy_static::lazy_static; +use perseus::prelude::*; +use serde::{Deserialize, Serialize}; +use sycamore::prelude::*; + +lazy_static! { + pub static ref LOGIN_FORM: Capsule = get_capsule(); +} + +#[auto_scope] +fn login_form_capsule( + cx: Scope, + state: &LoginFormStateRx, + props: LoginFormProps, +) -> View { + view! { + cx, + div (class="overflow-x-hidden overflow-y-auto fixed h-modal md:h-full top-4 left-0 right-0 md:inset-0 z-50 justify-center items-center"){ + div (class="relative w-full max-w-md px-4 h-full md:h-auto") { + div (class="bg-white rounded-lg shadow relative dark:bg-gray-700"){ + div (class="flex justify-end p-2"){ + button (class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-800 dark:hover:text-white"){ + "Back" + } + } + form (class="space-y-6 px-6 lg:px-8 pb-4 sm:pb-6 xl:pb-8") { + h3 (class="text-xl font-medium text-gray-900 dark:text-white"){"Sign in to our platform"} + div { + label (class="text-sm font-medium text-gray-900 block mb-2 dark:text-gray-300") {"Your email"} + input (class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white") {} + } + div { + label (class="text-sm font-medium text-gray-900 block mb-2 dark:text-gray-300"){"Your password"} + input (class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white"){} + } + div (class="flex justify-between"){ + div (class="flex items-start"){ + div (class="flex items-center h-5"){ + input (class="bg-gray-50 border border-gray-300 focus:ring-3 focus:ring-blue-300 h-4 w-4 rounded dark:bg-gray-600 dark:border-gray-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800") {} + } + div (class="text-sm ml-3"){ + label (class="font-medium text-gray-900 dark:text-gray-300"){"Remember me"} + } + } + a (class="text-sm text-blue-700 hover:underline dark:text-blue-500"){"Lost Password?"} + } + button (class="w-full text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"){"Login to your account"} + div (class="text-sm font-medium text-gray-500 dark:text-gray-300"){ + a (class="text-blue-700 hover:underline dark:text-blue-500"){"Create account"} + } + } + } + } + } + } +} + +#[derive(Serialize, Deserialize, Clone, ReactiveState)] +#[rx(alias = "LoginFormStateRx")] +struct LoginFormState { + username: String, + password: String, +} + +#[derive(Clone)] +pub struct LoginFormProps { + pub remember_me: bool, + pub endpoint: String, + pub lost_password_url: Option, + pub forgot_password_url: Option, +} + +pub fn get_capsule() -> Capsule { + Capsule::build(Template::build("login_form").build_state_fn(get_build_state)) + .empty_fallback() + .view_with_state(login_form_capsule) + .build() +} + +#[engine_only_fn] +async fn get_build_state(_info: StateGeneratorInfo<()>) -> LoginFormState { + LoginFormState { + username: "".to_string(), + password: "".to_string(), + } +} diff --git a/src/capsules/mod.rs b/src/capsules/mod.rs new file mode 100644 index 0000000..1f54584 --- /dev/null +++ b/src/capsules/mod.rs @@ -0,0 +1 @@ +pub mod login_form; diff --git a/src/components/layout.rs b/src/components/layout.rs index 2558932..e7a814e 100644 --- a/src/components/layout.rs +++ b/src/components/layout.rs @@ -1,4 +1,7 @@ -use crate::templates::global_state::{AppStateRx, LoginState}; +use crate::{ + capsules::login_form::{LoginFormProps, LOGIN_FORM}, + templates::global_state::{AppStateRx, LoginState}, +}; use perseus::prelude::*; use sycamore::prelude::*; use web_sys::Event; @@ -46,6 +49,7 @@ pub fn Layout<'a, G: Html>( div(class = "text-gray-700 text-2xl font-semibold py-2") { "Pool Elo - Season 1" } + div(class = "flex-1 py-2") {( match *global_state.auth.state.get() { LoginState::NotAuthenticated => { @@ -83,49 +87,14 @@ pub fn Layout<'a, G: Html>( ( match *global_state.auth.state.get() { LoginState::Authenticated => { view! { cx, - - - - div (class="overflow-x-hidden overflow-y-auto fixed h-modal md:h-full top-4 left-0 right-0 md:inset-0 z-50 justify-center items-center"){ - div (class="relative w-full max-w-md px-4 h-full md:h-auto") { - - div (class="bg-white rounded-lg shadow relative dark:bg-gray-700"){ - - div (class="flex justify-end p-2"){ - button (class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-800 dark:hover:text-white"){ - "Back" - } - } - - form (class="space-y-6 px-6 lg:px-8 pb-4 sm:pb-6 xl:pb-8") { - h3 (class="text-xl font-medium text-gray-900 dark:text-white"){"Sign in to our platform"} - div { - label (class="text-sm font-medium text-gray-900 block mb-2 dark:text-gray-300") {"Your email"} - input (class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white") {} - } - div { - label (class="text-sm font-medium text-gray-900 block mb-2 dark:text-gray-300"){"Your password"} - input (class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white"){} - } - div (class="flex justify-between"){ - div (class="flex items-start"){ - div (class="flex items-center h-5"){ - input (class="bg-gray-50 border border-gray-300 focus:ring-3 focus:ring-blue-300 h-4 w-4 rounded dark:bg-gray-600 dark:border-gray-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800") {} - } - div (class="text-sm ml-3"){ - label (class="font-medium text-gray-900 dark:text-gray-300"){"Remember me"} - } - } - a (class="text-sm text-blue-700 hover:underline dark:text-blue-500"){"Lost Password?"} - } - button (class="w-full text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"){"Login to your account"} - div (class="text-sm font-medium text-gray-500 dark:text-gray-300"){ - a (class="text-blue-700 hover:underline dark:text-blue-500"){"Create account"} - } - } - } - } - } + (LOGIN_FORM.widget(cx, "", + LoginFormProps{ + remember_me: true, + endpoint: "".to_string(), + lost_password_url: Some("".to_string()), + forgot_password_url: Some("".to_string()) + }) + ) }}, _ => { view! { cx, div {} } }}) diff --git a/src/main.rs b/src/main.rs index 8d738bf..f09b598 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +mod capsules; mod components; mod endpoints; #[allow(unused_imports)] @@ -61,6 +62,7 @@ pub fn main() -> PerseusApp { .template(crate::templates::add_game_form::get_template()) .template(crate::templates::one_v_one_board::get_template()) .template(crate::templates::overall_board::get_template()) + .capsule_ref(&*crate::capsules::login_form::LOGIN_FORM) .error_views(crate::error_views::get_error_views()) .index_view(|cx| { view! { cx,