Compare commits

...
12 Commits
Author SHA1 Message Date
Lucas Vas 467f8c9020 Merge pull request #76 from R15-Software-Dev/chore/api-err-return
Make student eligibility return an error
2026-03-25 13:42:25 -04:00
alphafetus 909f33b044 chore: make student eligibility return an error on missing access token 2026-03-25 13:41:10 -04:00
Lucas Vas 44450f407c Merge pull request #75 from R15-Software-Dev/chore/close-providers
Close provider forms
2026-03-25 13:31:14 -04:00
Lucas Vas 70a86f4545 Merge pull request #74 from R15-Software-Dev/feat/file-input-errors
Create error message display for file inputs
2026-03-25 13:28:23 -04:00
alphafetus e6409cc423 chore: close provider forms 2026-03-25 13:20:58 -04:00
alphafetus 4e3161a05f feat: create error message display for file inputs 2026-03-25 13:17:43 -04:00
Lucas Vas 51dfa013df Merge pull request #73 from R15-Software-Dev/fix/blank-id-requests
Fix for blank IDs in submissions
2026-03-25 12:58:55 -04:00
alphafetus 1d41b6022e fix: hopeful fix for blank IDs in submissions 2026-03-25 12:39:50 -04:00
Lucas Vas f9d5b25436 Merge pull request #72 from R15-Software-Dev/fix/filtering-bugs
Check requirement return value on Ok variant
2026-03-23 12:05:22 -04:00
alphafetus 891e324186 fix: check requirement return value on Ok variant 2026-03-23 11:47:06 -04:00
Lucas Vas b502c2f96d Merge pull request #71 from R15-Software-Dev/fix/comparison-filtering
Fix comparison logic for filtering
2026-03-19 13:49:06 -04:00
alphafetus 681da8c326 fix: use correct comparison logic
Was originally short-circuiting on empty lists of requirements.
2026-03-19 13:37:11 -04:00
4 changed files with 75 additions and 53 deletions

No files matched your search

+7 -7
View File
@@ -1,11 +1,11 @@
use crate::components::ToastList; use crate::components::ToastList;
use crate::components::login::ProviderLoginContext; use crate::components::login::ProviderLoginContext;
use crate::pages::student;
use crate::pages::{ use crate::pages::{
AdminHomePage, AdminProviderPage, AdminScholarshipPage, AdminShell, AdminUtilsPage, AdminHomePage, AdminProviderPage, AdminScholarshipPage, AdminShell, AdminUtilsPage,
ApplicantsPageFallback, AuthCallbackPage, LoanerBorrowForm, LoanerFallback, LoanerReturnForm, ApplicantsPageFallback, AuthCallbackPage, LoanerBorrowForm, LoanerFallback, LoanerReturnForm,
LoanerShell, ProviderContactPage, ProviderPortal, ScholarshipInfoPage, LoanerShell, ProviderContactPage, ProviderPortal,
}; };
use crate::pages::{FormClosedPage, student};
use leptos::prelude::*; use leptos::prelude::*;
use leptos_meta::{MetaTags, Stylesheet, Title, provide_meta_context}; use leptos_meta::{MetaTags, Stylesheet, Title, provide_meta_context};
use leptos_oidc::AuthSignal; use leptos_oidc::AuthSignal;
@@ -89,11 +89,11 @@ fn ProviderRoutes() -> impl MatchNestedRoutes + Clone {
<Route path=path!("/applicants") view=ApplicantsPageFallback /> <Route path=path!("/applicants") view=ApplicantsPageFallback />
<Route path=path!("/callback") view=AuthCallbackPage /> <Route path=path!("/callback") view=AuthCallbackPage />
<Route path=path!("/profile") view=ProviderContactPage /> <Route path=path!("/profile") view=ProviderContactPage />
// <Route path=path!("/scholarships") view=FormClosedPage /> <Route path=path!("/scholarships") view=FormClosedPage />
<ParentRoute path=path!("/scholarships") view=ScholarshipInfoPage> // <ParentRoute path=path!("/scholarships") view=ScholarshipInfoPage>
<Route path=path!(":id") view=ScholarshipInfoPage /> // <Route path=path!(":id") view=ScholarshipInfoPage />
<Route path=path!("") view=ScholarshipInfoPage /> // <Route path=path!("") view=ScholarshipInfoPage />
</ParentRoute> // </ParentRoute>
</ParentRoute> </ParentRoute>
} }
.into_inner() .into_inner()
+15 -3
View File
@@ -1,6 +1,6 @@
use crate::pages::api::files::{DeleteFile, upload_file}; use crate::pages::api::files::{DeleteFile, upload_file};
use leptos::html::{Input, Label}; use leptos::html::{Input, Label};
use leptos::logging::debug_log; use leptos::logging::{debug_error, debug_log};
use leptos::prelude::*; use leptos::prelude::*;
use leptos::web_sys::{File, FormData}; use leptos::web_sys::{File, FormData};
use leptos_icons::Icon; use leptos_icons::Icon;
@@ -28,6 +28,7 @@ pub fn FileDrop(
let file_name_list = RwSignal::new(Vec::new()); let file_name_list = RwSignal::new(Vec::new());
let hovering = RwSignal::new(false); let hovering = RwSignal::new(false);
let error_msg = RwSignal::new(String::new());
let name = StoredValue::new(name); let name = StoredValue::new(name);
let form_id = StoredValue::new(form_id); let form_id = StoredValue::new(form_id);
let auth = expect_context::<AuthSignal>(); let auth = expect_context::<AuthSignal>();
@@ -172,10 +173,18 @@ pub fn FileDrop(
existing_files existing_files
.get() .get()
.map(|result| { .map(|result| {
if let Ok(files) = result { match result {
Ok(files) => {
error_msg.set("".to_string());
file_name_list.set(files); file_name_list.set(files);
} }
}) Err(err) => {
let msg = err.to_string();
debug_error!("Error while getting files: {}", msg);
error_msg.set(msg);
}
}
});
}} }}
<label <label
node_ref=zone_ref node_ref=zone_ref
@@ -194,6 +203,9 @@ pub fn FileDrop(
<div class="text-sm text-gray-400">"Or click to select a file"</div> <div class="text-sm text-gray-400">"Or click to select a file"</div>
<input node_ref=input_ref type="file" class="hidden" on:change=on_input_change /> <input node_ref=input_ref type="file" class="hidden" on:change=on_input_change />
</label> </label>
<Show when=move || !error_msg.get().is_empty()>
<div class="flex-1">{error_msg}</div>
</Show>
<For <For
each=move || file_name_list.get() each=move || file_name_list.get()
key=|file| file.clone() key=|file| file.clone()
+34 -25
View File
@@ -4,7 +4,7 @@ use crate::pages::api::files::list_files;
use crate::pages::api::students::get_all_student_data; use crate::pages::api::students::get_all_student_data;
use crate::pages::api::{get_all_scholarship_info, get_comparison_info}; use crate::pages::api::{get_all_scholarship_info, get_comparison_info};
use crate::utils::get_user_claims; use crate::utils::get_user_claims;
use leptos::logging::debug_log; use leptos::logging::{debug_error, debug_log};
use leptos::prelude::*; use leptos::prelude::*;
use leptos_oidc::AuthSignal; use leptos_oidc::AuthSignal;
@@ -23,6 +23,11 @@ pub fn StudentEligibilityPage() -> impl IntoView {
let auth = expect_context::<AuthSignal>(); let auth = expect_context::<AuthSignal>();
let user_claims = get_user_claims(); let user_claims = get_user_claims();
let user_id = Memo::new(move |_| user_claims.get().map(|info| info.claims.subject.clone())); let user_id = Memo::new(move |_| user_claims.get().map(|info| info.claims.subject.clone()));
let access_token = Memo::new(move |_| auth.get().authenticated().map(|a| a.access_token()));
Effect::new(move || {
debug_log!("User ID was updated: {}", user_id.get().unwrap_or_default());
});
let form_id = StoredValue::new("scholarship_essays".to_string()); let form_id = StoredValue::new("scholarship_essays".to_string());
@@ -63,8 +68,7 @@ pub fn StudentEligibilityPage() -> impl IntoView {
.get("name") .get("name")
.unwrap_or(&ValueType::String(None)) .unwrap_or(&ValueType::String(None))
.as_string() .as_string()
.ok() .ok()?
.flatten()
.unwrap_or_default(); .unwrap_or_default();
debug_log!("Checking scholarship: {}", scholarship_name); debug_log!("Checking scholarship: {}", scholarship_name);
let requirements_map = scholarship let requirements_map = scholarship
@@ -72,8 +76,7 @@ pub fn StudentEligibilityPage() -> impl IntoView {
.get("requirements") .get("requirements")
.unwrap_or(&ValueType::Map(None)) .unwrap_or(&ValueType::Map(None))
.as_map() .as_map()
.ok() .ok()?
.flatten()
.unwrap_or_default() .unwrap_or_default()
.values() .values()
.cloned() .cloned()
@@ -108,38 +111,44 @@ pub fn StudentEligibilityPage() -> impl IntoView {
&& resolved_requirements && resolved_requirements
.iter() .iter()
.all(|list| { .all(|list| {
debug_log!("Checking list: {:?}", list);
if list.is_empty() {
true
} else {
list.iter() list.iter()
.fold( .any(|requirement| {
false, let result = requirement.compare(&student_info);
|prev, requirement| { match result.as_ref() {
let result = requirement Ok(status) => {
.compare(&student_info) debug_log!(
.unwrap_or_else(|err| { "Requirement with id {} is Ok({})", requirement.id, status
debug_log!("Requirement failed: {err}"); );
status.clone()
}
Err(e) => {
debug_error!(
"Requirement with id {} failed: {}", requirement.id, e
);
false false
}); }
if prev { prev } else { result } }
}, })
) }
}); });
if valid { if valid {
debug_log!("Scholarship is valid."); debug_log!("Scholarship is valid.");
let scholarship_id = StoredValue::new(scholarship.subject.clone()); let scholarship_id = StoredValue::new(scholarship.subject.clone());
let resource = Resource::new( let resource = Resource::new(
move || ( move || (
auth access_token.get(),
.try_with(|a| a.authenticated().map(|a| a.access_token()))
.flatten(),
form_id.get_value(), form_id.get_value(),
scholarship_id.get_value(), scholarship_id.get_value(),
), ),
async move |(access_token, form_id, scholarship_id)| { async move |(access_token, form_id, scholarship_id)| {
list_files( let Some(access_token) = access_token else {
access_token.unwrap_or_default(), return Err(ServerFnError::new("Couldn't find access token"));
form_id, };
scholarship_id, list_files(access_token, form_id, scholarship_id).await
)
.await
}, },
); );
Some((scholarship.clone(), resource)) Some((scholarship.clone(), resource))
+19 -18
View File
@@ -1,10 +1,10 @@
use std::collections::HashMap;
use leptos::prelude::*;
use uuid::Uuid;
use crate::common::ValueType; use crate::common::ValueType;
use crate::components::{Toast, ToastContext}; use crate::components::{Toast, ToastContext};
use crate::pages::api::students::{get_student_data, PutStudentData}; use crate::pages::api::students::{PutStudentData, get_student_data};
use crate::utils::get_user_claims; use crate::utils::get_user_claims;
use leptos::prelude::*;
use std::collections::HashMap;
use uuid::Uuid;
pub struct StudentFormInfo { pub struct StudentFormInfo {
pub data_map: RwSignal<HashMap<String, ValueType>>, pub data_map: RwSignal<HashMap<String, ValueType>>,
@@ -22,20 +22,19 @@ pub fn use_student_form(
let form_type = form_type.into(); let form_type = form_type.into();
let user_claims = get_user_claims(); let user_claims = get_user_claims();
let user_id = Memo::new(move |_| { let user_id = Memo::new(move |_| user_claims.get().map(|info| info.claims.subject.clone()));
user_claims.get()
.map(|info| info.claims.subject.clone())
});
let data_map = RwSignal::new(HashMap::new()); let data_map = RwSignal::new(HashMap::new());
let refresh_trigger = Trigger::new(); let refresh_trigger = Trigger::new();
let data_resource = Resource::new( let data_resource = Resource::new(
move || (user_id.get().unwrap_or_default(), form_type.get(), refresh_trigger.track()), move || (user_id.get(), form_type.get(), refresh_trigger.track()),
async move |(id, form_type, _)| get_student_data( async move |(id, form_type, _)| {
id, let Some(id) = id else {
form_type, return Ok(HashMap::new());
).await };
get_student_data(id, form_type).await
},
); );
Effect::new(move || { Effect::new(move || {
@@ -50,11 +49,13 @@ pub fn use_student_form(
let submit_action = ServerAction::<PutStudentData>::new(); let submit_action = ServerAction::<PutStudentData>::new();
let on_submit = move || { let on_submit = move || {
if let Some(id) = user_id.get() {
submit_action.dispatch(PutStudentData { submit_action.dispatch(PutStudentData {
subject: user_id.get().unwrap_or_default(), subject: id,
data_type: form_type.get(), data_type: form_type.get(),
data_map: data_map.get() data_map: data_map.get(),
}); });
}
}; };
if enable_toasts { if enable_toasts {
@@ -74,13 +75,13 @@ pub fn use_student_form(
Err(e) => Toast::new() Err(e) => Toast::new()
.id(Uuid::new_v4()) .id(Uuid::new_v4())
.header("Submission Failed") .header("Submission Failed")
.msg(e.to_string()) .msg(e.to_string()),
}; };
toasts.toast(toast); toasts.toast(toast);
untrack(move || submit_action.clear()); untrack(move || submit_action.clear());
}, },
false false,
); );
} }
@@ -90,6 +91,6 @@ pub fn use_student_form(
data_resource, data_resource,
user_id, user_id,
submit_action: on_submit.into(), submit_action: on_submit.into(),
submit_pending: submit_action.pending() submit_pending: submit_action.pending(),
} }
} }