diff --git a/src/capsules/forgot_password_form.rs b/src/capsules/forgot_password_form.rs index d9d5e00..3e50a0f 100644 --- a/src/capsules/forgot_password_form.rs +++ b/src/capsules/forgot_password_form.rs @@ -4,6 +4,8 @@ use serde::{Deserialize, Serialize}; use sycamore::prelude::*; use web_sys::Event; +use crate::components::sub_components::error_block::ErrorBlock; + cfg_if::cfg_if! { if #[cfg(client)] { use crate::{ @@ -114,19 +116,8 @@ fn forgot_password_form_capsule( div (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"){"Forgot Password"} - (match state.error.get().as_ref() != "" { - true => { view!{cx, - div (role="alert") { - div (class="bg-red-500 text-white font-bold rounded-t px-4 py-2") { - "Error" - } - div (class="border border-t-0 border-red-400 rounded-b bg-red-100 px-4 py-3 text-red-700"){ - p {(state.error.get())} - } - } - }}, - false => {view!{cx,}}, - }) + // Add component for handling error messages + ErrorBlock(error = state.error.clone()) div { label (class="text-sm font-medium text-gray-900 block mb-2 dark:text-gray-300") {"Username"} diff --git a/src/capsules/login_form.rs b/src/capsules/login_form.rs index 364b3cb..8770d36 100644 --- a/src/capsules/login_form.rs +++ b/src/capsules/login_form.rs @@ -4,6 +4,8 @@ use serde::{Deserialize, Serialize}; use sycamore::prelude::*; use web_sys::Event; +use crate::components::sub_components::error_block::ErrorBlock; + cfg_if::cfg_if! { if #[cfg(client)] { use crate::{ @@ -150,19 +152,8 @@ fn login_form_capsule( div (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"} - (match state.error.get().as_ref() != "" { - true => { view!{cx, - div (role="alert") { - div (class="bg-red-500 text-white font-bold rounded-t px-4 py-2") { - "Error" - } - div (class="border border-t-0 border-red-400 rounded-b bg-red-100 px-4 py-3 text-red-700"){ - p {(state.error.get())} - } - } - }}, - false => {view!{cx,}}, - }) + // Add component for handling error messages + ErrorBlock(error = state.error.clone()) div { label (class="text-sm font-medium text-gray-900 block mb-2 dark:text-gray-300") {"Username"} diff --git a/src/capsules/register_form.rs b/src/capsules/register_form.rs index 8b9dd60..87f8b47 100644 --- a/src/capsules/register_form.rs +++ b/src/capsules/register_form.rs @@ -4,6 +4,8 @@ use serde::{Deserialize, Serialize}; use sycamore::prelude::*; use web_sys::Event; +use crate::components::sub_components::error_block::ErrorBlock; + cfg_if::cfg_if! { if #[cfg(client)] { use crate::{ @@ -125,20 +127,8 @@ fn register_form_capsule( div (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"){"Register"} - - (match state.error.get().as_ref() != "" { - true => { view!{cx, - div (role="alert") { - div (class="bg-red-500 text-white font-bold rounded-t px-4 py-2") { - "Error" - } - div (class="border border-t-0 border-red-400 rounded-b bg-red-100 px-4 py-3 text-red-700"){ - p {(state.error.get())} - } - } - }}, - false => {view!{cx,}}, - }) + // Add component for handling error messages + ErrorBlock(error = state.error.clone()) div { label (class="text-sm font-medium text-gray-900 block mb-2 dark:text-gray-300") {"Username"} diff --git a/src/components/mod.rs b/src/components/mod.rs index f70771d..9e82fad 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1,2 +1,4 @@ mod header; pub mod layout; +pub mod static_components; +pub mod sub_components; diff --git a/src/components/static_components/close_button_path.rs b/src/components/static_components/close_button_path.rs new file mode 100644 index 0000000..e1df776 --- /dev/null +++ b/src/components/static_components/close_button_path.rs @@ -0,0 +1,9 @@ +use perseus::prelude::*; +use sycamore::prelude::*; + +#[component] +pub fn CloseButtonPath(cx: Scope) -> View { + view! { cx, + path (d="M14.348 14.849a1.2 1.2 0 0 1-1.697 0L10 11.819l-2.651 3.029a1.2 1.2 0 1 1-1.697-1.697l2.758-3.15-2.759-3.152a1.2 1.2 0 1 1 1.697-1.697L10 8.183l2.651-3.031a1.2 1.2 0 1 1 1.697 1.697l-2.758 3.152 2.758 3.15a1.2 1.2 0 0 1 0 1.698z"){} + } +} diff --git a/src/components/static_components/mod.rs b/src/components/static_components/mod.rs new file mode 100644 index 0000000..590f273 --- /dev/null +++ b/src/components/static_components/mod.rs @@ -0,0 +1 @@ +pub mod close_button_path; diff --git a/src/components/sub_components/error_block.rs b/src/components/sub_components/error_block.rs new file mode 100644 index 0000000..a55bbd5 --- /dev/null +++ b/src/components/sub_components/error_block.rs @@ -0,0 +1,38 @@ +use perseus::prelude::*; +use sycamore::prelude::*; +use web_sys::Event; + +use crate::components::static_components::close_button_path::CloseButtonPath; + +#[component(inline_props)] +pub fn ErrorBlock<'a, G: Html>(cx: Scope<'a>, error: RcSignal) -> View { + let error = create_ref(cx, error); + let is_empty = create_selector(cx, || error.get().is_empty()); + + let close_block = move |_event: Event| { + #[cfg(client)] + { + spawn_local_scoped(cx, async move { error.set(String::new()) }); + } + }; + + view! { cx, + (match !(*is_empty.get()) { + true => { view!{cx, + + div (class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative", role="alert") { + span (class="block sm:inline"){ + p {(*error.get())} + } + span (class="absolute top-0 bottom-0 right-0 px-4 py-3"){ + svg (on:click = close_block, class="fill-current h-6 w-6 text-red-500", role="button", viewBox="0 0 20 20") { + title {"Close"} + CloseButtonPath {} + } + } + } + }}, + false => {view!{cx,}}, + }) + } +} diff --git a/src/components/sub_components/mod.rs b/src/components/sub_components/mod.rs new file mode 100644 index 0000000..6d2012d --- /dev/null +++ b/src/components/sub_components/mod.rs @@ -0,0 +1 @@ +pub mod error_block; diff --git a/src/main.rs b/src/main.rs index 4477403..caa410a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -70,10 +70,10 @@ pub fn main() -> PerseusApp { PerseusApp::new() .global_state_creator(crate::global_state::get_global_state_creator()) - .template(crate::templates::index::get_template()) - .template(crate::templates::add_game_form::get_template()) - .template(crate::templates::one_v_one_board::get_template()) - .template(crate::templates::overall_board::get_template()) + .template(crate::templates::pool::index::get_template()) + .template(crate::templates::pool::add_game_form::get_template()) + .template(crate::templates::pool::one_v_one_board::get_template()) + .template(crate::templates::pool::overall_board::get_template()) .capsule_ref(&*crate::capsules::login_form::LOGIN_FORM) .capsule_ref(&*crate::capsules::forgot_password_form::FORGOT_PASSWORD_FORM) .capsule_ref(&*crate::capsules::register_form::REGISTER_FORM) diff --git a/src/templates/global_state.rs b/src/templates/global_state.rs deleted file mode 100644 index b146152..0000000 --- a/src/templates/global_state.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Not a page, global state that is shared between all pages - -use perseus::{prelude::*, state::GlobalStateCreator}; -use serde::{Deserialize, Serialize}; - -use crate::{ - models::auth::Claims, - state_enums::{LoginState, OpenState}, -}; - -#[derive(Serialize, Deserialize, ReactiveState, Clone)] -#[rx(alias = "AppStateRx")] -pub struct AppState { - #[rx(nested)] - pub auth: AuthData, - #[rx(nested)] - pub modals_open: ModalOpenData, -} - -#[derive(Serialize, Deserialize, ReactiveState, Clone)] -#[rx(alias = "AuthDataRx")] -pub struct AuthData { - pub state: LoginState, - pub username: Option, - pub claims: Claims, -} - -#[derive(Serialize, Deserialize, ReactiveState, Clone)] -#[rx(alias = "ModalOpenDataRx")] -pub struct ModalOpenData { - pub login: OpenState, -} - -pub fn get_global_state_creator() -> GlobalStateCreator { - GlobalStateCreator::new().build_state_fn(get_build_state) -} - -#[engine_only_fn] -pub async fn get_build_state() -> AppState { - AppState { - auth: AuthData { - state: LoginState::Unknown, - username: None, - claims: Claims { - sub: "".to_owned(), - exp: 0, - }, - }, - modals_open: ModalOpenData { - login: OpenState::Closed, - }, - } -} - -// Client only code to check if they're authenticated -#[cfg(client)] -impl AuthDataRx { - pub fn detect_state(&self) { - // If the user is in a known state, return - if let LoginState::Authenticated | LoginState::NotAuthenticated = *self.state.get() { - return; - } - // TODO -> Get state from storage - self.state.set(LoginState::NotAuthenticated); - } -} diff --git a/src/templates/mod.rs b/src/templates/mod.rs index d6460a6..951ffb5 100644 --- a/src/templates/mod.rs +++ b/src/templates/mod.rs @@ -1,14 +1,21 @@ -pub mod add_game_form; -pub mod index; -pub mod one_v_one_board; -pub mod overall_board; +pub mod pickleball; +pub mod pool; +pub mod table_tennis; +pub mod user; #[cfg(client)] use perseus::utils::get_path_prefix_client; #[cfg(client)] pub fn get_api_path(path: &str) -> String { - let origin = web_sys::window().unwrap().origin(); - let base_path = get_path_prefix_client(); - format!("{}{}{}", origin, base_path, path) + #[cfg(engine)] + { + path.to_string() + } + #[cfg(client)] + { + let origin = web_sys::window().unwrap().origin(); + let base_path = get_path_prefix_client(); + format!("{}{}{}", origin, base_path, path) + } } diff --git a/src/templates/pickleball/mod.rs b/src/templates/pickleball/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/templates/add_game_form.rs b/src/templates/pool/add_game_form.rs similarity index 100% rename from src/templates/add_game_form.rs rename to src/templates/pool/add_game_form.rs diff --git a/src/templates/index.rs b/src/templates/pool/index.rs similarity index 100% rename from src/templates/index.rs rename to src/templates/pool/index.rs diff --git a/src/templates/pool/mod.rs b/src/templates/pool/mod.rs new file mode 100644 index 0000000..f6e01b3 --- /dev/null +++ b/src/templates/pool/mod.rs @@ -0,0 +1,4 @@ +pub mod add_game_form; +pub mod index; +pub mod one_v_one_board; +pub mod overall_board; diff --git a/src/templates/one_v_one_board.rs b/src/templates/pool/one_v_one_board.rs similarity index 100% rename from src/templates/one_v_one_board.rs rename to src/templates/pool/one_v_one_board.rs diff --git a/src/templates/overall_board.rs b/src/templates/pool/overall_board.rs similarity index 100% rename from src/templates/overall_board.rs rename to src/templates/pool/overall_board.rs diff --git a/src/templates/table_tennis/mod.rs b/src/templates/table_tennis/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/templates/user/mod.rs b/src/templates/user/mod.rs new file mode 100644 index 0000000..e69de29