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:
alphafetus committed 2025-09-19 12:30:27 -04:00
1 parent 0d4bbc9e70
commit 39996319ba
6 files changed
+88 -58

No files matched your search

+16 -11
View File
@@ -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)
}
+2 -2
View File
@@ -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<_>>()
}
+6
View File
@@ -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 {
+34 -19
View File
@@ -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
View File
@@ -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
+2 -2
View File
@@ -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>