From 82648cfbae619ad025409decea983c90f0d8b14d Mon Sep 17 00:00:00 2001 From: Matthew Kaminski Date: Thu, 29 Aug 2024 23:29:28 -0400 Subject: [PATCH 1/4] Add initial daisy ui --- .github/workflows/github-actions-demo.yml | 2 ++ README.md | 6 ++++++ package.json | 1 + src/capsules/login_form.rs | 2 +- tailwind.config.js | 21 +++++++++++++-------- 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index bdf7e78..2c85deb 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -40,6 +40,8 @@ jobs: run: npm install -g sass - name: Install tailwindcss via npm run: npm install -D tailwindcss + - name: Install daisy ui via npm + run: npm i -D daisyui@latest - name: Compile css run: npm run build # TODO -> Remove wasm-opt-version once perseus is updated diff --git a/README.md b/README.md index d894b8a..c1afbe0 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,17 @@ Run `curl --proto '=https' --tlsv1.3 https://sh.rustup.rs -sSf | sh` https://nodejs.org/en (todo look into:) https://pnpm.io/ +`npm i -D daisyui@latest` ### Unix based systems: `sudo apt install nodejs` `sudo apt install npm` +`npm i -D daisyui@latest` + +For easy UI see: + +https://daisyui.com/components/button/ ## 3. Install Perseus, for real-time updates while developing diff --git a/package.json b/package.json index 6e9af84..b9a09b9 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "author": "", "license": "ISC", "devDependencies": { + "daisyui": "^4.12.10", "tailwindcss": "^3.4.10" } } diff --git a/src/capsules/login_form.rs b/src/capsules/login_form.rs index 8770d36..9c44fab 100644 --- a/src/capsules/login_form.rs +++ b/src/capsules/login_form.rs @@ -179,7 +179,7 @@ fn login_form_capsule( }) button (on:click = handle_forgot_password, class="text-sm text-blue-700 hover:underline dark:text-blue-500"){"Lost Password?"} } - button (on:click = handle_log_in, 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"){"Log in"} + button (on:click = handle_log_in, class="btn"){"Log in"} div (class="text-sm font-medium text-gray-500 dark:text-gray-300"){ button (on:click = handle_register, class="text-blue-700 hover:underline dark:text-blue-500"){"Create account"} } diff --git a/tailwind.config.js b/tailwind.config.js index 596829f..5d57aca 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,14 +1,19 @@ +const { default: daisyui } = require("daisyui"); + module.exports = { purge: { - mode: "all", - content: [ - "./src/**/*.rs", - "./index.html", - "./src/**/*.html", - "./src/**/*.css", - ], + mode: "all", + content: [ + "./src/**/*.rs", + "./index.html", + "./src/**/*.html", + "./src/**/*.css", + ], }, theme: {}, variants: {}, - plugins: [], + plugins: [require("daisyui")], + daisyui: { + themes: ["light"], + }, }; From 97420b84c8b6b03d0d577a0d84d7afde591f606e Mon Sep 17 00:00:00 2001 From: Matthew Kaminski Date: Fri, 30 Aug 2024 01:10:24 -0400 Subject: [PATCH 2/4] Changed login to be daisyui --- src/capsules/login_form.rs | 94 ++++++++----------- .../static_components/close_button.rs | 28 ++++++ .../static_components/close_button_path.rs | 9 -- src/components/static_components/mod.rs | 2 +- src/components/sub_components/error_block.rs | 2 +- 5 files changed, 69 insertions(+), 66 deletions(-) create mode 100644 src/components/static_components/close_button.rs delete mode 100644 src/components/static_components/close_button_path.rs diff --git a/src/capsules/login_form.rs b/src/capsules/login_form.rs index 9c44fab..9a75d12 100644 --- a/src/capsules/login_form.rs +++ b/src/capsules/login_form.rs @@ -4,7 +4,9 @@ use serde::{Deserialize, Serialize}; use sycamore::prelude::*; use web_sys::Event; -use crate::components::sub_components::error_block::ErrorBlock; +use crate::components::{ + static_components::close_button::CloseButtonSvg, sub_components::error_block::ErrorBlock, +}; cfg_if::cfg_if! { if #[cfg(client)] { @@ -81,19 +83,6 @@ fn login_form_capsule( } }; - let handle_register = move |_event: Event| { - #[cfg(client)] - { - spawn_local_scoped(cx, async move { - let global_state = Reactor::::from_cx(cx).get_global_state::(cx); - global_state.modals_open.register.set(OpenState::Open); - // Close modal - state.reset(); - global_state.modals_open.login.set(OpenState::Closed); - }); - } - }; - let handle_log_in = move |_event: Event| { #[cfg(client)] { @@ -141,49 +130,44 @@ fn login_form_capsule( }; 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 md:mx-auto w-full md:w-1/2 lg:w-1/3 z-0 my-10") { - div (class="bg-white rounded-lg shadow relative dark:bg-gray-700"){ - div (class="flex justify-end p-2"){ - button (on:click = close_modal, 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"){ - "Close" - } - } - 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"} + dialog (class="modal-open modal modal-bottom sm:modal-middle") { + div (class="modal-box"){ + // Header row - title and close button + h3 (class="text-lg font-bold mb-4 text-center"){"Sign in"} + button (on:click = close_modal, class = "btn btn-circle right-2 top-2 absolute") { CloseButtonSvg {} } - // Add component for handling error messages - ErrorBlock(error = state.error.clone()) + // 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"} - input (bind:value = state.username, 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"){"Password"} - input (bind:value = state.password, type = "password", 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"){ - (match props.remember_me { - true => { view!{ cx, - div (class="flex items-start"){ - div (class="flex items-center h-5"){ - input (bind:checked = state.remember_me, type = "checkbox", class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600") {} - } - div (class="text-sm ml-3"){ - label (class="font-medium text-gray-900 dark:text-gray-300"){"Remember me"} - } - } - }}, - false => view!{cx, }, - }) - button (on:click = handle_forgot_password, class="text-sm text-blue-700 hover:underline dark:text-blue-500"){"Lost Password?"} - } - button (on:click = handle_log_in, class="btn"){"Log in"} - div (class="text-sm font-medium text-gray-500 dark:text-gray-300"){ - button (on:click = handle_register, class="text-blue-700 hover:underline dark:text-blue-500"){"Create account"} - } - } + // Username field + div (class = "label") { span (class = "label-text") { "Username" } } + input (bind:value = state.username, class = "input input-bordered w-full") + + // Password field + div (class = "label") { span (class = "label-text") { "Password" } } + input (bind:value = state.password, type = "password", class = "input input-bordered w-full") + + // Remember me button and forget password button + div (class="flex justify-between items-center mt-1"){ + // Remember me button + (match props.remember_me { + true => { view!{ cx, + div (class = "flex items-start form-control") { + label (class = "label cursor-pointer") { + span (class = "label-text mr-4") { "Remember me" } + input (bind:checked = state.remember_me, type = "checkbox", class = "checkbox") + } + } + }}, + false => view!{cx, }, + }) + // Forget password button + button (on:click = handle_forgot_password, class="flex link link-primary"){"Lost Password?"} + } + + // Log in button + div (class = "flex justify-center") { + button (on:click = handle_log_in, class="btn"){"Log in"} } } } diff --git a/src/components/static_components/close_button.rs b/src/components/static_components/close_button.rs new file mode 100644 index 0000000..a5438fc --- /dev/null +++ b/src/components/static_components/close_button.rs @@ -0,0 +1,28 @@ +use perseus::prelude::*; +use sycamore::prelude::*; + +#[component] +pub fn CloseButtonSvg(cx: Scope) -> View { + view! { cx, + svg ( + xmlns = "http://www.w3.org/2000/svg", + class = "h-6 w-6 stroke-primary", + fill = "none", + viewBox = "0 0 24 24", + ) { + path ( + stroke-linecap = "round", + stroke-linejoin = "round", + stroke-width = "2", + d = "M6 18L18 6M6 6l12 12" + ){} + } + } +} + +#[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/close_button_path.rs b/src/components/static_components/close_button_path.rs deleted file mode 100644 index e1df776..0000000 --- a/src/components/static_components/close_button_path.rs +++ /dev/null @@ -1,9 +0,0 @@ -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 index 590f273..0a28e4e 100644 --- a/src/components/static_components/mod.rs +++ b/src/components/static_components/mod.rs @@ -1 +1 @@ -pub mod close_button_path; +pub mod close_button; diff --git a/src/components/sub_components/error_block.rs b/src/components/sub_components/error_block.rs index a55bbd5..87d81e0 100644 --- a/src/components/sub_components/error_block.rs +++ b/src/components/sub_components/error_block.rs @@ -2,7 +2,7 @@ use perseus::prelude::*; use sycamore::prelude::*; use web_sys::Event; -use crate::components::static_components::close_button_path::CloseButtonPath; +use crate::components::static_components::close_button::CloseButtonPath; #[component(inline_props)] pub fn ErrorBlock<'a, G: Html>(cx: Scope<'a>, error: RcSignal) -> View { From d82a5e2c6545a56d6a9710572f0e89c39b636c7e Mon Sep 17 00:00:00 2001 From: Matthew Kaminski Date: Fri, 30 Aug 2024 01:25:42 -0400 Subject: [PATCH 3/4] Updated alert to daisyui --- .../static_components/close_button.rs | 7 ------ src/components/static_components/indicator.rs | 22 ++++++++++++++++ src/components/static_components/mod.rs | 1 + src/components/sub_components/error_block.rs | 25 +++++-------------- 4 files changed, 29 insertions(+), 26 deletions(-) create mode 100644 src/components/static_components/indicator.rs diff --git a/src/components/static_components/close_button.rs b/src/components/static_components/close_button.rs index a5438fc..15ebb88 100644 --- a/src/components/static_components/close_button.rs +++ b/src/components/static_components/close_button.rs @@ -19,10 +19,3 @@ pub fn CloseButtonSvg(cx: Scope) -> View { } } } - -#[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/indicator.rs b/src/components/static_components/indicator.rs new file mode 100644 index 0000000..8325eb1 --- /dev/null +++ b/src/components/static_components/indicator.rs @@ -0,0 +1,22 @@ +use perseus::prelude::*; +use sycamore::prelude::*; + +#[component] +pub fn ErrorSvg(cx: Scope) -> View { + view! { cx, + svg ( + xmlns="http://www.w3.org/2000/svg", + class="h-6 w-6 shrink-0 stroke-current", + fill="none", + viewBox="0 0 24 24", + ) + { + path ( + stroke-linecap="round", + stroke-linejoin="round", + stroke-width="2", + d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z", + ){} + } + } +} diff --git a/src/components/static_components/mod.rs b/src/components/static_components/mod.rs index 0a28e4e..39eaca8 100644 --- a/src/components/static_components/mod.rs +++ b/src/components/static_components/mod.rs @@ -1 +1,2 @@ pub mod close_button; +pub mod indicator; diff --git a/src/components/sub_components/error_block.rs b/src/components/sub_components/error_block.rs index 87d81e0..2f5135d 100644 --- a/src/components/sub_components/error_block.rs +++ b/src/components/sub_components/error_block.rs @@ -1,35 +1,22 @@ use perseus::prelude::*; use sycamore::prelude::*; -use web_sys::Event; -use crate::components::static_components::close_button::CloseButtonPath; +use crate::components::static_components::indicator::ErrorSvg; #[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 (role="alert", class="alert alert-error") { + // Error icon + ErrorSvg {} - 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 {} - } - } + // Error text + span {(*error.get())} } }}, false => {view!{cx,}}, From a288a35225591ea0aef8086f09a66a7364bd0ced Mon Sep 17 00:00:00 2001 From: Matthew Kaminski Date: Fri, 30 Aug 2024 02:16:15 -0400 Subject: [PATCH 4/4] Updated remaining modals to daisyui Made username carry over between modals too --- src/capsules/forgot_password_form.rs | 54 ++++++++------- src/capsules/login_form.rs | 24 ++++++- src/capsules/register_form.rs | 100 +++++++++++++-------------- 3 files changed, 100 insertions(+), 78 deletions(-) diff --git a/src/capsules/forgot_password_form.rs b/src/capsules/forgot_password_form.rs index 3e50a0f..b6a9969 100644 --- a/src/capsules/forgot_password_form.rs +++ b/src/capsules/forgot_password_form.rs @@ -4,14 +4,18 @@ use serde::{Deserialize, Serialize}; use sycamore::prelude::*; use web_sys::Event; -use crate::components::sub_components::error_block::ErrorBlock; +use crate::{ + components::{ + static_components::close_button::CloseButtonSvg, sub_components::error_block::ErrorBlock, + }, + global_state::AppStateRx, +}; cfg_if::cfg_if! { if #[cfg(client)] { use crate::{ state_enums::{ OpenState}, templates::get_api_path, - global_state::{AppStateRx}, endpoints::FORGOT_PASSWORD, models::{ auth::ForgotPasswordRequest, @@ -54,6 +58,13 @@ fn forgot_password_form_capsule( state: &ForgotPasswordFormStateRx, _props: ForgotPasswordFormProps, ) -> View { + // If there's a tentative username, set it + let global_state = Reactor::::from_cx(cx).get_global_state::(cx); + if let Some(username) = (*global_state.auth.username.get()).clone() { + state.username.set(username); + global_state.auth.username.set(None); + } + let close_modal = move |_event: Event| { #[cfg(client)] { @@ -105,31 +116,26 @@ fn forgot_password_form_capsule( }; 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 md:mx-auto w-full md:w-1/2 lg:w-1/3 z-0 my-10") { - div (class="bg-white rounded-lg shadow relative dark:bg-gray-700"){ - div (class="flex justify-end p-2"){ - button (on:click = close_modal, 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"){ - "Close" - } - } - 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"} + dialog (class="modal-open modal modal-bottom sm:modal-middle animate-none") { + div (class="modal-box"){ + // Header row - title and close button + h3 (class="text-lg font-bold mb-4 text-center"){"Forgot Password"} + button (on:click = close_modal, class = "btn btn-circle right-2 top-2 absolute") { CloseButtonSvg {} } - // Add component for handling error messages - ErrorBlock(error = state.error.clone()) + // 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"} - input (bind:value = state.username, 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"){"Contact Info"} - input (bind:value = state.how_to_reach, 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"){} - } + // Username field + div (class = "label") { span (class = "label-text") { "Username" } } + input (bind:value = state.username, class = "input input-bordered w-full") - button (on:click = handle_submit, 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"){"Submit"} - } + // Password field + div (class = "label") { span (class = "label-text") { "Contact Info" } } + input (bind:value = state.how_to_reach, class = "input input-bordered w-full") + + // Submit button + div (class = "flex justify-center") { + button (on:click = handle_submit, class="btn"){"Submit"} } } } diff --git a/src/capsules/login_form.rs b/src/capsules/login_form.rs index 9a75d12..ac0c29b 100644 --- a/src/capsules/login_form.rs +++ b/src/capsules/login_form.rs @@ -4,15 +4,17 @@ use serde::{Deserialize, Serialize}; use sycamore::prelude::*; use web_sys::Event; -use crate::components::{ - static_components::close_button::CloseButtonSvg, sub_components::error_block::ErrorBlock, +use crate::{ + components::{ + static_components::close_button::CloseButtonSvg, sub_components::error_block::ErrorBlock, + }, + global_state::AppStateRx, }; cfg_if::cfg_if! { if #[cfg(client)] { use crate::{ endpoints::LOGIN, - global_state::{AppStateRx}, models::auth::{LoginInfo, LoginResponse, WebAuthInfo}, models::generic::GenericResponse, state_enums::{OpenState}, @@ -56,6 +58,13 @@ fn login_form_capsule( state: &LoginFormStateRx, props: LoginFormProps, ) -> View { + // If there's a tentative username, set it + let global_state = Reactor::::from_cx(cx).get_global_state::(cx); + if let Some(username) = (*global_state.auth.username.get()).clone() { + state.username.set(username); + global_state.auth.username.set(None); + } + let close_modal = move |_event: Event| { #[cfg(client)] { @@ -72,10 +81,19 @@ fn login_form_capsule( { spawn_local_scoped(cx, async move { let global_state = Reactor::::from_cx(cx).get_global_state::(cx); + + // Update tentative username + global_state + .auth + .username + .set(Some((*state.username.get()).clone())); + + // Open new modal global_state .modals_open .forgot_password .set(OpenState::Open); + // Close modal state.reset(); global_state.modals_open.login.set(OpenState::Closed); diff --git a/src/capsules/register_form.rs b/src/capsules/register_form.rs index 87f8b47..6c53ee4 100644 --- a/src/capsules/register_form.rs +++ b/src/capsules/register_form.rs @@ -4,7 +4,9 @@ use serde::{Deserialize, Serialize}; use sycamore::prelude::*; use web_sys::Event; -use crate::components::sub_components::error_block::ErrorBlock; +use crate::components::{ + static_components::close_button::CloseButtonSvg, sub_components::error_block::ErrorBlock, +}; cfg_if::cfg_if! { if #[cfg(client)] { @@ -104,9 +106,14 @@ fn register_form_capsule( return; } + // Update tentative username + global_state + .auth + .username + .set(Some((*state.username.get()).clone())); + // Open login modal global_state.modals_open.login.set(OpenState::Open); - state.reset(); // Close modal state.reset(); @@ -116,57 +123,48 @@ fn register_form_capsule( }; 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 md:mx-auto w-full md:w-1/2 lg:w-1/3 z-0 my-10") { - div (class="bg-white rounded-lg shadow relative dark:bg-gray-700"){ - div (class="flex justify-end p-2"){ - button (on:click = close_modal, 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"){ - "Close" - } - } - 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"} + dialog (class="modal-open modal modal-bottom sm:modal-middle"){ + div (class="modal-box") { + // Header row - title and close button + h3 (class="text-lg font-bold mb-4 text-center"){"Register"} + button (on:click = close_modal, class = "btn btn-circle right-2 top-2 absolute") { CloseButtonSvg {} } - // Add component for handling error messages - ErrorBlock(error = state.error.clone()) + // 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"} - input (bind:value = state.username, 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"){"Password"} - input (bind:value = state.password, type = "password", 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"){} - } - (match props.registration_code { - true => { view!{cx, - div { - label (class="text-sm font-medium text-gray-900 block mb-2 dark:text-gray-300"){"Registration code"} - input (bind:value = state.registration_code, 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"){} - } - }}, - false => {view!{cx,}}, - }) - (match props.nickname { - true => { view!{cx, - div { - label (class="text-sm font-medium text-gray-900 block mb-2 dark:text-gray-300"){"Nickname (optional)"} - input (bind:value = state.nickname, 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"){} - } - }}, - false => {view!{cx,}}, - }) - (match props.email { - true => { view!{cx, - div { - label (class="text-sm font-medium text-gray-900 block mb-2 dark:text-gray-300"){"Email (optional)"} - input (bind:value = state.email, 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"){} - } - }}, - false => {view!{cx,}}, - }) - button (on:click = handle_register, 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"){"Register"} - } + // Username field + div (class = "label") { span (class = "label-text") { "Username" } } + input (bind:value = state.username, class = "input input-bordered w-full") + + // Password field + div (class = "label") { span (class = "label-text") { "Password" } } + input (bind:value = state.password, type = "password", class = "input input-bordered w-full") + + (match props.registration_code { + true => { view! {cx, + div (class = "label") { span (class = "label-text") { "Registration Code" } } + input (bind:value = state.registration_code, class = "input input-bordered w-full") + }}, + false => {view!{cx,}}, + }) + (match props.nickname { + true => { view! {cx, + div (class = "label") { span (class = "label-text") { "Nickname (Optional)" } } + input (bind:value = state.nickname, class = "input input-bordered w-full") + }}, + false => {view!{cx,}}, + }) + (match props.email { + true => { view! {cx, + div (class = "label") { span (class = "label-text") { "Email (Optional)" } } + input (bind:value = state.email, class = "input input-bordered w-full") + }}, + false => {view!{cx,}}, + }) + + // Register button + div (class = "flex justify-center") { + button (on:click = handle_register, class="btn"){"Register"} } } }