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