Refactor all components to use expandable data #10
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