feat: alter OutlinedTextField to use ValueType enum
Also implemented extra error checking when the values are passed as the incorrect type. For example, the only accepted types are strings and numbers, so that will be checked and handled accordingly.
This commit is contained in:
1 parent
0d4bbc9e70
commit
39996319ba
6 files changed
+89
-59
No files matched your search
+16
-11
@@ -1,11 +1,12 @@
|
||||
use crate::components::MultiEntryData;
|
||||
use crate::components::{MultiEntryData, ValueType};
|
||||
use macros::Reactive;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::default::Default;
|
||||
use chrono::{DateTime, Utc};
|
||||
use reactive_stores::Store;
|
||||
|
||||
/// Represents information about a student.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Reactive, Default)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default, Store, Reactive)]
|
||||
pub struct StudentInfo {
|
||||
/// The email address of the student. This was used in the previous version of the database,
|
||||
/// and should now be deprecated.
|
||||
@@ -14,32 +15,36 @@ pub struct StudentInfo {
|
||||
pub Email: String,
|
||||
/// The student's first name.
|
||||
#[serde(default)]
|
||||
pub first_name: String,
|
||||
pub first_name: ValueType, // String
|
||||
/// The student's last name.
|
||||
#[serde(default)]
|
||||
pub last_name: String,
|
||||
pub last_name: ValueType, // String
|
||||
/// The student's highest math score on the SAT.
|
||||
#[serde(default)]
|
||||
pub math_sat: i32,
|
||||
#[serde(default = "default_number")]
|
||||
pub math_sat: ValueType, // Number
|
||||
/// The student's desired contact email.
|
||||
#[serde(default)]
|
||||
pub contact_email: String,
|
||||
pub contact_email: ValueType, // String
|
||||
/// The student's address.
|
||||
#[serde(default)]
|
||||
pub address: String,
|
||||
pub address: ValueType, // String
|
||||
/// The student's town of residence.
|
||||
#[serde(default)]
|
||||
pub town: String,
|
||||
pub town: ValueType, // String
|
||||
/// The student's entered phone number.
|
||||
#[serde(default)]
|
||||
pub phone_number: String, // May be stored as a straight number in the future.
|
||||
pub phone_number: ValueType, // May be stored as a straight number in the future, but String
|
||||
/// The student's birthdate.
|
||||
#[serde(default)]
|
||||
pub birth_date: DateTime<Utc>,
|
||||
/// The student's gender.
|
||||
#[serde(default)]
|
||||
pub gender: String,
|
||||
pub gender: ValueType, // String
|
||||
/// The student's athletic information.
|
||||
#[serde(default)]
|
||||
pub athletic_info: Vec<MultiEntryData>,
|
||||
}
|
||||
|
||||
fn default_number() -> ValueType {
|
||||
ValueType::Number(0)
|
||||
}
|
||||
@@ -9,7 +9,7 @@ use super::{multi_entry_data::*, multi_entry_member::*};
|
||||
fn render_entry_component(entry_data: MultiEntryData, member_info: MultiEntryMember, label: String) -> impl IntoView {
|
||||
// Parse the multientry members into the correct components
|
||||
let data = entry_data.data.get(&member_info.member_name);
|
||||
let reactive = RwSignal::new(data.unwrap().as_string().unwrap());
|
||||
let reactive = RwSignal::new(data.unwrap().clone());
|
||||
let temp = either!(member_info.input_type,
|
||||
InputType::Text => view! {
|
||||
<OutlinedTextField
|
||||
@@ -60,7 +60,7 @@ pub fn Entry(
|
||||
let data = entry_data.clone();
|
||||
let input = input_type.clone();
|
||||
view! {
|
||||
<div>{render_entry_component(data, input, "test".to_owned())}</div>
|
||||
// <div>{render_entry_component(data, input, "test".to_owned())}</div>
|
||||
}
|
||||
}).collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
@@ -59,6 +59,12 @@ impl ValueType {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ValueType {
|
||||
fn default() -> Self {
|
||||
ValueType::String(String::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ValueType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let str = match self {
|
||||
|
||||
@@ -1,32 +1,42 @@
|
||||
use leptos::prelude::*;
|
||||
use std::str::FromStr;
|
||||
use crate::components::ValueType;
|
||||
|
||||
#[component]
|
||||
pub fn OutlinedTextField<T>(
|
||||
#[prop(optional, into)] placeholder: T,
|
||||
#[prop(optional)] value: RwSignal<T>,
|
||||
pub fn OutlinedTextField(
|
||||
#[prop(optional, into)] placeholder: String,
|
||||
// #[prop()] value: Subfield<TStore, TInner, ValueType>,
|
||||
#[prop()] value: RwSignal<ValueType>,
|
||||
#[prop(optional)] disabled: RwSignal<bool>,
|
||||
#[prop(optional)] error: RwSignal<bool>,
|
||||
#[prop(optional, into)] name: String,
|
||||
#[prop(optional, into)] label: String,
|
||||
#[prop(optional, into)] input_type: String,
|
||||
) -> impl IntoView
|
||||
where
|
||||
T: Default + 'static + FromStr + ToString + PartialEq + Send + Sync + Clone,
|
||||
{
|
||||
// Parsing logic for non-string types which happens on each input.
|
||||
#[prop(optional, into)] error_text: RwSignal<String>
|
||||
) -> impl IntoView {
|
||||
// Check if the type is one of the supported formats. This input will only accept a string
|
||||
// or a number value.
|
||||
let on_input = move |e| {
|
||||
let input_value = event_target_value(&e);
|
||||
let can_parse = T::from_str(&input_value);
|
||||
if let Ok(parsed_value) = can_parse {
|
||||
value.set(parsed_value);
|
||||
} else {
|
||||
error.set(true);
|
||||
value.set(T::default());
|
||||
match value.get() {
|
||||
ValueType::String(_) => {
|
||||
// Define string parse function.
|
||||
value.set(ValueType::String(event_target_value(&e)));
|
||||
},
|
||||
ValueType::Number(_) => {
|
||||
// Define number parse function.
|
||||
let temp = event_target_value(&e);
|
||||
value.set(ValueType::Number(temp.parse::<i32>().unwrap_or(0)));
|
||||
},
|
||||
_ => {
|
||||
// Create some custom function that reports failure.
|
||||
let value_type = value.get();
|
||||
println!("Input {value_type:?} is not a valid type.");
|
||||
error.set(true);
|
||||
error_text.set(format!("InputType {value_type:?} is not a valid type."));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
view! (
|
||||
|
||||
view! {
|
||||
<div class="flex flex-1">
|
||||
<label class="flex flex-col flex-1">
|
||||
<span class="block ml-1.5 mb-0 font-bold">{label}</span>
|
||||
@@ -37,12 +47,17 @@ where
|
||||
disabled:border-gray-600 disabled:pointer-events-none disabled:bg-gray-600/33"
|
||||
r#type={input_type}
|
||||
disabled={disabled}
|
||||
placeholder={placeholder.to_string()}
|
||||
placeholder={placeholder}
|
||||
prop:name=name
|
||||
prop:value={move || value.get().to_string()} // Sets the initial value.
|
||||
on:input=on_input
|
||||
/>
|
||||
<div
|
||||
class="text-red-600 text-sm mr-1.5 ml-1.5"
|
||||
class=(["hidden"], move || !error.get())>
|
||||
{error_text}
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
+28
-24
@@ -7,10 +7,11 @@ use serde_dynamo::{from_item, to_item};
|
||||
|
||||
use crate::app::Unauthenticated;
|
||||
use crate::common::{StudentInfo, UserClaims};
|
||||
use crate::components::{ActionButton, Loading, OutlinedTextField, Panel, RadioList, Row, Select};
|
||||
use crate::components::{ActionButton, Loading, OutlinedTextField, Panel, Row};
|
||||
use leptos::leptos_dom::logging::console_log;
|
||||
use leptos::prelude::*;
|
||||
use leptos_oidc::{Algorithm, AuthLoaded, AuthSignal, Authenticated};
|
||||
use reactive_stores::Store;
|
||||
use traits::{AsReactive, ReactiveCapture};
|
||||
|
||||
/// # Get Student Info
|
||||
@@ -124,6 +125,7 @@ pub fn HomePage() -> impl IntoView {
|
||||
server_resource
|
||||
.get()
|
||||
.map(|submission: StudentInfo| {
|
||||
let store_info = Store::new(submission.clone());
|
||||
let reactive_info = submission.as_reactive();
|
||||
let elements_disabled = RwSignal::new(false);
|
||||
let result_msg = Signal::derive(move || {
|
||||
@@ -197,34 +199,36 @@ pub fn HomePage() -> impl IntoView {
|
||||
value=reactive_info.address
|
||||
/>
|
||||
</Row>
|
||||
<Row>
|
||||
<RadioList
|
||||
label="Town:"
|
||||
selected=reactive_info.town
|
||||
items=vec!["Southbury", "Middlebury"]
|
||||
.into_iter()
|
||||
.map(|s| s.into())
|
||||
.collect()
|
||||
disabled=elements_disabled
|
||||
/>
|
||||
</Row>
|
||||
<Row>
|
||||
<Select
|
||||
label="Gender:"
|
||||
value_list=vec!["Male", "Female", "Prefer not to answer"]
|
||||
.into_iter()
|
||||
.map(|s| s.into())
|
||||
.collect()
|
||||
value=reactive_info.gender
|
||||
disabled=elements_disabled
|
||||
/>
|
||||
</Row>
|
||||
// <Row>
|
||||
// <RadioList
|
||||
// label="Town:"
|
||||
// selected=reactive_info.town
|
||||
// items=vec!["Southbury", "Middlebury"]
|
||||
// .into_iter()
|
||||
// .map(|s| s.into())
|
||||
// .collect()
|
||||
// disabled=elements_disabled
|
||||
// />
|
||||
// </Row>
|
||||
// <Row>
|
||||
// <Select
|
||||
// label="Gender:"
|
||||
// value_list=vec!["Male", "Female", "Prefer not to answer"]
|
||||
// .into_iter()
|
||||
// .map(|s| s.into())
|
||||
// .collect()
|
||||
// value=reactive_info.gender
|
||||
// disabled=elements_disabled
|
||||
// />
|
||||
// </Row>
|
||||
<Row>
|
||||
<ActionButton
|
||||
on:click=move |_| {
|
||||
let captured = reactive_info.capture();
|
||||
console_log(format!("Found values: {captured:?}").as_str());
|
||||
submit_action
|
||||
.dispatch(CreateSampleSubmission {
|
||||
student_info: reactive_info.capture(),
|
||||
student_info: captured,
|
||||
});
|
||||
}
|
||||
disabled=elements_disabled
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::app::Unauthenticated;
|
||||
use crate::common::UserClaims;
|
||||
use crate::components::{Loading, MultiEntry, MultiEntryData, MultiEntryMember, OutlinedTextField};
|
||||
use crate::components::{Loading, MultiEntry, MultiEntryData, MultiEntryMember, OutlinedTextField, ValueType};
|
||||
use leptos::leptos_dom::logging::console_log;
|
||||
use leptos::prelude::*;
|
||||
use leptos_oidc::{Algorithm, AuthLoaded, AuthSignal, Authenticated, LogoutLink};
|
||||
@@ -28,7 +28,7 @@ pub fn ProviderEntry() -> impl IntoView {
|
||||
placeholder = "Testing information..."
|
||||
name = "testing_input"
|
||||
label = "This is a testing question."
|
||||
value = RwSignal::new("".to_owned())
|
||||
value = RwSignal::new(ValueType::String("".to_owned()))
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
Reference in new issue
Block a user