From cc0827f271bba11933c605e97d274fe5f19c3a07 Mon Sep 17 00:00:00 2001 From: Austin Alvarado Date: Sat, 27 Jan 2024 09:10:02 -0700 Subject: [PATCH] app: update forms to use new components (#818) --- app/src/components/change_password.rs | 112 ++++------- app/src/components/create_group.rs | 46 ++--- app/src/components/create_user.rs | 207 ++++++--------------- app/src/components/form/field.rs | 48 ++--- app/src/components/form/mod.rs | 1 + app/src/components/form/static_value.rs | 26 +++ app/src/components/form/submit.rs | 28 +-- app/src/components/login.rs | 119 ++++++------ app/src/components/reset_password_step2.rs | 77 +++----- app/src/components/user_details_form.rs | 155 +++++---------- 10 files changed, 294 insertions(+), 525 deletions(-) create mode 100644 app/src/components/form/static_value.rs diff --git a/app/src/components/change_password.rs b/app/src/components/change_password.rs index 296fd9d..642fb1a 100644 --- a/app/src/components/change_password.rs +++ b/app/src/components/change_password.rs @@ -1,5 +1,8 @@ use crate::{ - components::router::{AppRoute, Link}, + components::{ + form::{field::Field, submit::Submit}, + router::{AppRoute, Link}, + }, infra::{ api::HostService, common_component::{CommonComponent, CommonComponentParts}, @@ -207,7 +210,6 @@ impl Component for ChangePasswordForm { fn view(&self, ctx: &Context) -> Html { let is_admin = ctx.props().is_admin; let link = ctx.link(); - type Field = yew_form::Field; html! { <>
@@ -224,90 +226,44 @@ impl Component for ChangePasswordForm { } } else { html! {} } } -
+ {if !is_admin { html! { -
- -
- -
- {&self.form.field_message("old_password")} -
-
-
+ + form={&self.form} + required=true + label="Current password" + field_name="old_password" + input_type="password" + autocomplete="current-password" + oninput={link.callback(|_| Msg::FormUpdate)} /> }} else { html! {} }} -
- -
- -
- {&self.form.field_message("password")} -
-
-
-
- -
- -
- {&self.form.field_message("confirm_password")} -
-
-
-
- + + form={&self.form} + required=true + label="New password" + field_name="password" + input_type="password" + autocomplete="new-password" + oninput={link.callback(|_| Msg::FormUpdate)} /> + + form={&self.form} + required=true + label="Confirm password" + field_name="confirm_password" + input_type="password" + autocomplete="new-password" + oninput={link.callback(|_| Msg::FormUpdate)} /> + {"Back"} -
+ } diff --git a/app/src/components/create_group.rs b/app/src/components/create_group.rs index a7e6641..3282007 100644 --- a/app/src/components/create_group.rs +++ b/app/src/components/create_group.rs @@ -1,5 +1,8 @@ use crate::{ - components::router::AppRoute, + components::{ + form::{field::Field, submit::Submit}, + router::AppRoute, + }, infra::common_component::{CommonComponent, CommonComponentParts}, }; use anyhow::{bail, Result}; @@ -93,44 +96,21 @@ impl Component for CreateGroupForm { fn view(&self, ctx: &Context) -> Html { let link = ctx.link(); - type Field = yew_form::Field; html! {
{"Create a group"}
-
- -
- -
- {&self.form.field_message("groupname")} -
-
-
-
- -
+ + form={&self.form} + required=true + label="Group name" + field_name="groupname" + oninput={link.callback(|_| Msg::Update)} /> + { if let Some(e) = &self.common.error { html! { diff --git a/app/src/components/create_user.rs b/app/src/components/create_user.rs index c8c8939..a5cca69 100644 --- a/app/src/components/create_user.rs +++ b/app/src/components/create_user.rs @@ -1,5 +1,8 @@ use crate::{ - components::router::AppRoute, + components::{ + form::{field::Field, submit::Submit}, + router::AppRoute, + }, infra::{ api::HostService, common_component::{CommonComponent, CommonComponentParts}, @@ -187,163 +190,57 @@ impl Component for CreateUserForm { fn view(&self, ctx: &Context) -> Html { let link = &ctx.link(); - type Field = yew_form::Field; html! {
-
-
{"Create a user"}
-
-
- -
- -
- {&self.form.field_message("username")} -
-
-
-
- -
- -
- {&self.form.field_message("email")} -
-
-
-
- -
- -
- {&self.form.field_message("display_name")} -
-
-
-
- -
- -
- {&self.form.field_message("first_name")} -
-
-
-
- -
- -
- {&self.form.field_message("last_name")} -
-
-
-
- -
- -
- {&self.form.field_message("password")} -
-
-
-
- -
- -
- {&self.form.field_message("confirm_password")} -
-
-
-
- -
+ + form={&self.form} + required=true + label="User name" + field_name="username" + oninput={link.callback(|_| Msg::Update)} /> + + form={&self.form} + required=true + label="Email" + field_name="email" + input_type="email" + oninput={link.callback(|_| Msg::Update)} /> + + form={&self.form} + label="Display name" + field_name="display_name" + autocomplete="name" + oninput={link.callback(|_| Msg::Update)} /> + + form={&self.form} + label="First name" + field_name="first_name" + autocomplete="given-name" + oninput={link.callback(|_| Msg::Update)} /> + + form={&self.form} + label="Last name" + field_name="last_name" + autocomplete="family-name" + oninput={link.callback(|_| Msg::Update)} /> + + form={&self.form} + label="Password" + field_name="password" + input_type="password" + autocomplete="new-password" + oninput={link.callback(|_| Msg::Update)} /> + + form={&self.form} + label="Confirm password" + field_name="confirm_password" + input_type="password" + autocomplete="new-password" + oninput={link.callback(|_| Msg::Update)} /> + { if let Some(e) = &self.common.error { diff --git a/app/src/components/form/field.rs b/app/src/components/form/field.rs index ab5018f..7ae385f 100644 --- a/app/src/components/form/field.rs +++ b/app/src/components/form/field.rs @@ -8,6 +8,11 @@ pub struct Props { pub form: Form, #[prop_or(false)] pub required: bool, + #[prop_or(String::from("text"))] + pub input_type: String, + // If not present, will default to field_name + #[prop_or(None)] + pub autocomplete: Option, #[prop_or_else(Callback::noop)] pub oninput: Callback, } @@ -15,28 +20,29 @@ pub struct Props { #[function_component(Field)] pub fn field(props: &Props) -> Html { html! { -
- -
- - form={&props.form} - field_name={props.field_name.clone()} - class="form-control" - class_invalid="is-invalid has-error" - class_valid="has-success" - autocomplete={props.field_name.clone()} - oninput={&props.oninput} /> -
- {&props.form.field_message(&props.field_name)} -
+
+ +
+ + form={&props.form} + field_name={props.field_name.clone()} + input_type={props.input_type.clone()} + class="form-control" + class_invalid="is-invalid has-error" + class_valid="has-success" + autocomplete={props.autocomplete.clone().unwrap_or(props.field_name.clone())} + oninput={&props.oninput} /> +
+ {&props.form.field_message(&props.field_name)}
+
} } diff --git a/app/src/components/form/mod.rs b/app/src/components/form/mod.rs index dc112e3..f46bded 100644 --- a/app/src/components/form/mod.rs +++ b/app/src/components/form/mod.rs @@ -1,4 +1,5 @@ pub mod checkbox; pub mod field; pub mod select; +pub mod static_value; pub mod submit; diff --git a/app/src/components/form/static_value.rs b/app/src/components/form/static_value.rs new file mode 100644 index 0000000..85874cc --- /dev/null +++ b/app/src/components/form/static_value.rs @@ -0,0 +1,26 @@ +use yew::{function_component, html, virtual_dom::AttrValue, Children, Properties}; + +#[derive(Properties, PartialEq)] +pub struct Props { + pub label: AttrValue, + pub id: AttrValue, + pub children: Children, +} + +#[function_component(StaticValue)] +pub fn static_value(props: &Props) -> Html { + html! { +
+ +
+ + {for props.children.iter()} + +
+
+ } +} diff --git a/app/src/components/form/submit.rs b/app/src/components/form/submit.rs index 622917f..2911440 100644 --- a/app/src/components/form/submit.rs +++ b/app/src/components/form/submit.rs @@ -1,24 +1,30 @@ use web_sys::MouseEvent; -use yew::{function_component, html, Callback, Properties}; +use yew::{function_component, html, virtual_dom::AttrValue, Callback, Children, Properties}; #[derive(Properties, PartialEq)] pub struct Props { pub disabled: bool, pub onclick: Callback, + // Additional elements to insert after the button, in the same div + #[prop_or_default] + pub children: Children, + #[prop_or(AttrValue::from("Submit"))] + pub text: AttrValue, } #[function_component(Submit)] pub fn submit(props: &Props) -> Html { html! { -
- -
+
+ + {for props.children.iter()} +
} } diff --git a/app/src/components/login.rs b/app/src/components/login.rs index 8d35be8..b78ffc5 100644 --- a/app/src/components/login.rs +++ b/app/src/components/login.rs @@ -1,5 +1,8 @@ use crate::{ - components::router::{AppRoute, Link}, + components::{ + form::submit::Submit, + router::{AppRoute, Link}, + }, infra::{ api::HostService, common_component::{CommonComponent, CommonComponentParts}, @@ -155,68 +158,62 @@ impl Component for LoginForm { } } else { html! { -
-
-
- - - -
- + +
+
+ + +
-
-
- - - -
- -
-
- - { if password_reset_enabled { - html! { - - {"Forgot your password?"} - - } - } else { - html!{} - }} -
-
- { if let Some(e) = &self.common.error { - html! { e.to_string() } - } else { html! {} } - } + +
+
+
+ + +
+ +
+ + { if password_reset_enabled { + html! { + + {"Forgot your password?"} + + } + } else { + html!{} + }} + +
+ { if let Some(e) = &self.common.error { + html! { e.to_string() } + } else { html! {} } + } +
} } diff --git a/app/src/components/reset_password_step2.rs b/app/src/components/reset_password_step2.rs index 7dfece6..a63cdb9 100644 --- a/app/src/components/reset_password_step2.rs +++ b/app/src/components/reset_password_step2.rs @@ -1,5 +1,8 @@ use crate::{ - components::router::{AppRoute, Link}, + components::{ + form::{field::Field, submit::Submit}, + router::{AppRoute, Link}, + }, infra::{ api::HostService, common_component::{CommonComponent, CommonComponentParts}, @@ -164,61 +167,29 @@ impl Component for ResetPasswordStep2Form { } _ => (), }; - type Field = yew_form::Field; html! { <>

{"Reset your password"}

-
-
- -
- -
- {&self.form.field_message("password")} -
-
-
-
- -
- -
- {&self.form.field_message("confirm_password")} -
-
-
-
- -
+ + + label="New password" + required=true + form={&self.form} + field_name="password" + autocomplete="new-password" + input_type="password" + oninput={link.callback(|_| Msg::FormUpdate)} /> + + label="Confirm password" + required=true + form={&self.form} + field_name="confirm_password" + autocomplete="new-password" + input_type="password" + oninput={link.callback(|_| Msg::FormUpdate)} /> + { if let Some(e) = &self.common.error { html! { diff --git a/app/src/components/user_details_form.rs b/app/src/components/user_details_form.rs index 56100c6..663e78b 100644 --- a/app/src/components/user_details_form.rs +++ b/app/src/components/user_details_form.rs @@ -1,7 +1,10 @@ use std::str::FromStr; use crate::{ - components::user_details::User, + components::{ + form::{field::Field, static_value::StaticValue, submit::Submit}, + user_details::User, + }, infra::common_component::{CommonComponent, CommonComponentParts}, }; use anyhow::{bail, Error, Result}; @@ -183,7 +186,6 @@ impl Component for UserDetailsForm { } fn view(&self, ctx: &Context) -> Html { - type Field = yew_form::Field; let link = &ctx.link(); let avatar_string = match &self.avatar { @@ -196,107 +198,40 @@ impl Component for UserDetailsForm { html! {
-
- -
- {&self.user.id} -
-
-
- -
- {&self.user.creation_date.naive_local().date()} -
-
-
- -
- {&self.user.uuid} -
-
-
- -
- -
- {&self.form.field_message("email")} -
-
-
-
- -
- -
- {&self.form.field_message("display_name")} -
-
-
-
- -
- -
- {&self.form.field_message("first_name")} -
-
-
-
- -
- -
- {&self.form.field_message("last_name")} -
-
-
+ + {&self.user.id} + + + {&self.user.creation_date.naive_local().date()} + + + {&self.user.uuid} + + + form={&self.form} + required=true + label="Email" + field_name="email" + input_type="email" + oninput={link.callback(|_| Msg::Update)} /> + + form={&self.form} + label="Display name" + field_name="display_name" + autocomplete="name" + oninput={link.callback(|_| Msg::Update)} /> + + form={&self.form} + label="First name" + field_name="first_name" + autocomplete="given-name" + oninput={link.callback(|_| Msg::Update)} /> + + form={&self.form} + label="Last name" + field_name="last_name" + autocomplete="family-name" + oninput={link.callback(|_| Msg::Update)} />
-
- -
+ { if let Some(e) = &self.common.error {