diff --git a/src/app.rs b/src/app.rs index bc50b33..a517025 100644 --- a/src/app.rs +++ b/src/app.rs @@ -136,14 +136,8 @@ fn StudentRoutes() -> impl MatchNestedRoutes + Clone { path=path!("additional/work-experience") view=student::StudentWorkExperiencePage /> - - + + } .into_inner() diff --git a/src/common/form_schema.rs b/src/common/form_schema.rs index f7bca1e..dc1099f 100644 --- a/src/common/form_schema.rs +++ b/src/common/form_schema.rs @@ -36,60 +36,70 @@ impl InputType { ) -> impl IntoView { view! { {match self { - InputType::Text(member, label, placeholder, required) => + InputType::Text(member, label, placeholder, required) => { view! { - }.into_any(), - InputType::Number(member, label, placeholder, required) => + } + .into_any() + } + InputType::Number(member, label, placeholder, required) => { view! { - }.into_any(), - InputType::Checkbox(member, label, options, required) => + } + .into_any() + } + InputType::Checkbox(member, label, options, required) => { view! { - }.into_any(), - InputType::Radio(member, label, options, required) => + } + .into_any() + } + InputType::Radio(member, label, options, required) => { view! { - }.into_any(), - InputType::Select(member, label, options, required) => + } + .into_any() + } + InputType::Select(member, label, options, required) => { view! { ::new(); + let data_member = StoredValue::new(data_member); // Register this input's validation signal. let validator_context = use_validation_context() .expect("FormValidSignal was not found"); - - let raw_value = Signal::derive({ - let data_member = data_member.clone(); - move || - data_map - .get() - .get(&data_member) - .unwrap_or(&ValueType::String(None)) - .to_string() - }); + + let display_value = RwSignal::new( + data_map.get_untracked() + .get(&data_member.get_value()) + .map(|v| v.to_string()) + .unwrap_or_default() + ); let dirty = RwSignal::new(false); let error = Signal::derive(move || - validate(required.get(), &raw_value.get(), &input_type.get()) + validate(required.get(), &display_value.get(), &input_type.get()) ); let show_errors = Signal::derive(move || { dirty.get() && matches!(error.get(), ValidationState::Invalid(_)) }); - let validator = RwSignal::new(InputState::new(data_member.clone(), error.clone(), dirty.clone())); + let validator = RwSignal::new(InputState::new(data_member.get_value(), error.clone(), dirty.clone())); validator_context.validators.update(|list| list.push(validator)); @@ -106,35 +107,42 @@ pub fn OutlinedTextField( }); }); - let str_type = Signal::derive(move || match input_type.get_untracked() { - TextFieldType::Number => "number", - _ => "text" // This is fine even for emails, as we're doing our own validation. - }); - let on_blur = move |_| { // Check validity of input's current value. The value will be updated by on_input, // and it should be a raw input (not parsed into a ValueType). dirty.set(true); }; + // Syncs data from the map into the display_value, only when the map is actually changed. + Effect::new(move || { + let new_value = data_map.get() + .get(&data_member.get_value()) + .map(|v| v.to_string()) + .unwrap_or_default(); + + if new_value != display_value.get_untracked() { + debug_log!("Found value from map: {}", new_value); + display_value.set(new_value); + } + }); + // This function parses the input into the correct type. It only accepts numbers or strings as // valid types, and parses them accordingly. - let on_input = { - let data_member = data_member.clone(); - move |e| { - let to_parse = event_target_value(&e); + let on_input = move |e| { + let to_parse = event_target_value(&e); + debug_log!("Setting value to display_value: {}", to_parse); + display_value.set(to_parse.clone()); + + // Push the new value to the data map. This happens regardless of whether the value parses + // or not. + data_map.update(|map| { let into_map = match input_type.get() { TextFieldType::Number => ValueType::Number(Some(to_parse)), _ => ValueType::String(Some(to_parse)) }; - // Edit the data map. Note that this should be uncoupled in the future - we don't want - // to update the map inside the component definition, but inside the page/form that - // created this input. - data_map.update(|map| { - map.insert(data_member.clone(), into_map); - }); - } + map.insert(data_member.get_value(), into_map); + }); }; view! { @@ -147,11 +155,11 @@ pub fn OutlinedTextField( transition-all duration-150 border-red-700 bg-transparent disabled:border-gray-600 disabled:pointer-events-none disabled:bg-gray-600/33" - r#type=str_type + r#type="text" disabled=disabled placeholder=placeholder prop:name=name - prop:value=raw_value + prop:value=display_value on:input=on_input on:blur=on_blur /> diff --git a/src/pages/admin/scholarship_page.rs b/src/pages/admin/scholarship_page.rs index c3b13ab..4b08b96 100644 --- a/src/pages/admin/scholarship_page.rs +++ b/src/pages/admin/scholarship_page.rs @@ -4,12 +4,8 @@ use leptos::prelude::*; pub fn AdminScholarshipPage() -> impl IntoView { view! {
-
- "Scholarships View" -
-
- "This is a placeholder for now. It will show a list of all scholarships." -
+
"Scholarships View"
+
"This is a placeholder for now. It will show a list of all scholarships."
} } \ No newline at end of file diff --git a/src/pages/student/academics_page.rs b/src/pages/student/academics_page.rs index f254903..7a04cbb 100644 --- a/src/pages/student/academics_page.rs +++ b/src/pages/student/academics_page.rs @@ -7,56 +7,57 @@ pub fn StudentAcademicsPage() -> impl IntoView { let controller = use_student_form("academics"); view! { - -
-
- - - - - - - -
-
- + + {move || controller.data_resource.get().map(|_| + view! { +
+
+ + + + + + + +
+
+ }).collect_view() + } + } } \ No newline at end of file diff --git a/src/pages/student/athletics_page.rs b/src/pages/student/athletics_page.rs index 3c74f87..61fb12b 100644 --- a/src/pages/student/athletics_page.rs +++ b/src/pages/student/athletics_page.rs @@ -8,10 +8,7 @@ pub fn StudentAthleticsPage() -> impl IntoView { let controller = use_student_form("athletics"); view! { - +
diff --git a/src/pages/student/eligibility_page.rs b/src/pages/student/eligibility_page.rs index e472472..9f761dc 100644 --- a/src/pages/student/eligibility_page.rs +++ b/src/pages/student/eligibility_page.rs @@ -60,53 +60,69 @@ pub fn StudentEligibilityPage() -> impl IntoView { let Some(Ok(student_info)) = student_resource.get() else { return view! {}.into_any(); }; - - let valid_list = scholarships_list.iter() + let valid_list = scholarships_list + .iter() .filter_map(|scholarship| { - let scholarship_name = scholarship.data.get("name") - .unwrap_or(&ValueType::String(None)) - .as_string().ok().flatten().unwrap_or_default(); + let scholarship_name = scholarship + .data + .get("name") + .unwrap_or(&ValueType::String(None)) + .as_string() + .ok() + .flatten() + .unwrap_or_default(); debug_log!("Checking scholarship: {}", scholarship_name); - - // Each scholarship will return Some(itself), or None. This is will be automatically - // filtered based on the information. Invalid scholarships are None, along with those - // that fail validation for any reason. Scholarships that are valid or did not choose - // any requirements are Some(scholarship). - let requirements_map = scholarship.data.get("requirements") + let requirements_map = scholarship + .data + .get("requirements") .unwrap_or(&ValueType::Map(None)) - .as_map().ok().flatten() + .as_map() + .ok() + .flatten() .unwrap_or_default() - .values().cloned() + .values() + .cloned() .collect::>(); - let resolved_requirements = requirements_map .iter() .map(|list_val| { - // We want to cast to a list, and then map the list to a new - // one that contains the correct ComparisonData. - let list = list_val.as_list().ok().flatten().unwrap_or_default(); - list.iter().filter_map(|v| { - let id_string = v.as_string().ok().flatten().unwrap_or_default(); - relations_list.iter() - .find(|relation| relation.id == id_string) - }) - .cloned() - .collect::>() + let list = list_val + .as_list() + .ok() + .flatten() + .unwrap_or_default(); + list.iter() + .filter_map(|v| { + let id_string = v + .as_string() + .ok() + .flatten() + .unwrap_or_default(); + relations_list + .iter() + .find(|relation| relation.id == id_string) + }) + .cloned() + .collect::>() }) .collect::>>(); - - debug_log!("Resolved requirements: {:?}", resolved_requirements.iter().flatten().map(|rel| rel.display_text.clone()).collect::>()); - - let valid = resolved_requirements.iter() + debug_log!( + "Resolved requirements: {:?}", resolved_requirements.iter().flatten().map(|rel| rel.display_text.clone()).collect::>() + ); + let valid = resolved_requirements + .iter() .all(|list| { - list.iter().fold(false, |prev, requirement| { - let result = requirement.compare(&student_info) - .unwrap_or(false); - - if prev { prev } else { result } - }) + list.iter() + .fold( + false, + |prev, requirement| { + let result = requirement + .compare(&student_info) + .unwrap_or(false); + if prev { prev } else { result } + }, + ) }); - if valid { debug_log!("Scholarship is valid."); Some(scholarship.clone()) @@ -116,15 +132,19 @@ pub fn StudentEligibilityPage() -> impl IntoView { }) .collect::>(); + // Each scholarship will return Some(itself), or None. This is will be automatically + // filtered based on the information. Invalid scholarships are None, along with those + // that fail validation for any reason. Scholarships that are valid or did not choose + // any requirements are Some(scholarship). + + // We want to cast to a list, and then map the list to a new + // one that contains the correct ComparisonData. + view! {
{format!("Valid scholarships: {}", valid_list.len())}
impl IntoView { } } /> - }.into_any() + } + .into_any() }}
diff --git a/src/pages/student/shell.rs b/src/pages/student/shell.rs index ba0c9d6..fe958d3 100644 --- a/src/pages/student/shell.rs +++ b/src/pages/student/shell.rs @@ -15,7 +15,7 @@ pub fn StudentShell() -> impl IntoView { title="R15 Student Scholarship Application" logo="/PHS_Stacked_Acronym.png" /> - +
@@ -38,7 +38,7 @@ pub fn StudentShell() -> impl IntoView { ], ), ), - TabInfo::new("Eligible Scholarships", "eligibility", None) + TabInfo::new("Eligible Scholarships", "eligibility", None), ] />