Merge pull request #68 from R15-Software-Dev/fix/comparison-changes

Comparison and sports changes
This commit was merged in pull request #68.
This commit is contained in:
Lucas Vas authored and GitHub committed 2026-03-11 12:49:53 -04:00
commit 448e8bbb38
4 files changed
+58 -51

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, LoanerShell, ProviderContactPage, ProviderPortal, ScholarshipInfoPage,
}; };
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=FormClosedPage> <ParentRoute path=path!("/scholarships") view=ScholarshipInfoPage>
// <Route path=path!(":id") view=FormClosedPage /> <Route path=path!(":id") view=ScholarshipInfoPage />
// <Route path=path!("") view=FormClosedPage /> <Route path=path!("") view=ScholarshipInfoPage />
// </ParentRoute> </ParentRoute>
</ParentRoute> </ParentRoute>
} }
.into_inner() .into_inner()
+26 -37
View File
@@ -14,10 +14,9 @@ use crate::common::{
ComparisonData, ComparisonType, MapListComparison, NumberComparison, NumberListComparison, ComparisonData, ComparisonType, MapListComparison, NumberComparison, NumberListComparison,
TextComparison, TextListComparison, ValueType, TextComparison, TextListComparison, ValueType,
}; };
use std::collections::HashMap;
use leptos::prelude::ServerFnError; use leptos::prelude::ServerFnError;
use leptos::server; use leptos::server;
use std::collections::HashMap;
/// Helper function to create all sports comparisons. Only used to create initial /// Helper function to create all sports comparisons. Only used to create initial
/// comparison data, and should be removed upon completion of a comparison editor page. /// comparison data, and should be removed upon completion of a comparison editor page.
@@ -250,14 +249,18 @@ fn create_major_comparisons() -> Vec<ComparisonData> {
let ids = display_texts let ids = display_texts
.iter() .iter()
.map(|text| .map(|text| {
format!("{}_{}", "major", text.to_lowercase().replace(" ", "_").replace("/", "_")) format!(
"{}_{}",
"major",
text.to_lowercase().replace(" ", "_").replace("/", "_")
) )
})
.collect::<Vec<String>>(); .collect::<Vec<String>>();
ids.iter().zip(display_texts).map(|(id, display_text)| { ids.iter()
create_major_comparison(id, display_text) .zip(display_texts)
}) .map(|(id, display_text)| create_major_comparison(id, display_text))
.collect::<Vec<ComparisonData>>() .collect::<Vec<ComparisonData>>()
} }
@@ -284,9 +287,7 @@ fn make_comp_list() -> Vec<ComparisonData> {
let service_hours_20 = ComparisonData::new( let service_hours_20 = ComparisonData::new(
"service_hours_20", "service_hours_20",
"service_hours", "service_hours",
ComparisonType::Number( ComparisonType::Number(NumberComparison::GreaterThanOrEqual),
NumberComparison::GreaterThanOrEqual
),
ValueType::Number(Some(20.to_string())), ValueType::Number(Some(20.to_string())),
"Community Service", "Community Service",
"20+ service hours", "20+ service hours",
@@ -295,9 +296,7 @@ fn make_comp_list() -> Vec<ComparisonData> {
let service_hours_25 = ComparisonData::new( let service_hours_25 = ComparisonData::new(
"service_hours_25", "service_hours_25",
"service_hours", "service_hours",
ComparisonType::Number( ComparisonType::Number(NumberComparison::GreaterThanOrEqual),
NumberComparison::GreaterThanOrEqual
),
ValueType::Number(Some(25.to_string())), ValueType::Number(Some(25.to_string())),
"Community Service", "Community Service",
"25+ service hours", "25+ service hours",
@@ -306,9 +305,7 @@ fn make_comp_list() -> Vec<ComparisonData> {
let service_hours_30 = ComparisonData::new( let service_hours_30 = ComparisonData::new(
"service_hours_30", "service_hours_30",
"service_hours", "service_hours",
ComparisonType::Number( ComparisonType::Number(NumberComparison::GreaterThanOrEqual),
NumberComparison::GreaterThanOrEqual
),
ValueType::Number(Some(30.to_string())), ValueType::Number(Some(30.to_string())),
"Community Service", "Community Service",
"30+ service hours", "30+ service hours",
@@ -334,7 +331,7 @@ fn make_comp_list() -> Vec<ComparisonData> {
let attended_bas = ComparisonData::new( let attended_bas = ComparisonData::new(
"attended_bas", "attended_bas",
"attended_bas", "attend_bas",
ComparisonType::Text(TextComparison::Matches), ComparisonType::Text(TextComparison::Matches),
ValueType::String(Some("Yes".to_string())), ValueType::String(Some("Yes".to_string())),
"Additional Eligibility Factors", "Additional Eligibility Factors",
@@ -365,7 +362,7 @@ fn make_comp_list() -> Vec<ComparisonData> {
ComparisonType::Text(TextComparison::Matches), ComparisonType::Text(TextComparison::Matches),
ValueType::String(Some("Yes".to_string())), ValueType::String(Some("Yes".to_string())),
"Additional Eligibility Factors", "Additional Eligibility Factors",
"Participated in Pomperaug Youth Baseball" "Participated in Pomperaug Youth Baseball",
); );
let aquatic_club = ComparisonData::new( let aquatic_club = ComparisonData::new(
@@ -374,7 +371,7 @@ fn make_comp_list() -> Vec<ComparisonData> {
ComparisonType::Text(TextComparison::Matches), ComparisonType::Text(TextComparison::Matches),
ValueType::String(Some("Yes".to_string())), ValueType::String(Some("Yes".to_string())),
"Additional Eligibility Factors", "Additional Eligibility Factors",
"Participated in Panthers Aquatic Club" "Participated in Panthers Aquatic Club",
); );
let pea_member = ComparisonData::new( let pea_member = ComparisonData::new(
@@ -383,7 +380,7 @@ fn make_comp_list() -> Vec<ComparisonData> {
ComparisonType::Text(TextComparison::Matches), ComparisonType::Text(TextComparison::Matches),
ValueType::String(Some("Yes".to_string())), ValueType::String(Some("Yes".to_string())),
"Additional Eligibility Factors", "Additional Eligibility Factors",
"Has a Parent in the PEA" "Has a Parent in the PEA",
); );
let music_program = ComparisonData::new( let music_program = ComparisonData::new(
@@ -392,7 +389,7 @@ fn make_comp_list() -> Vec<ComparisonData> {
ComparisonType::Text(TextComparison::Matches), ComparisonType::Text(TextComparison::Matches),
ValueType::String(Some("Yes".to_string())), ValueType::String(Some("Yes".to_string())),
"Sports Participation", "Sports Participation",
"PHS Music Program" "PHS Music Program",
); );
let mut sports_comps = create_sports_comparisons(); let mut sports_comps = create_sports_comparisons();
@@ -412,7 +409,7 @@ fn make_comp_list() -> Vec<ComparisonData> {
youth_baseball, youth_baseball,
aquatic_club, aquatic_club,
pea_member, pea_member,
music_program music_program,
]; ];
comp_list.append(&mut sports_comps); comp_list.append(&mut sports_comps);
@@ -453,12 +450,7 @@ pub async fn get_comparison_info() -> Result<Vec<ComparisonData>, ServerFnError>
// Query the database for all comparisons. The client is only going to use the // Query the database for all comparisons. The client is only going to use the
// id and display text, but we'll return the whole thing. // id and display text, but we'll return the whole thing.
match client match client.scan().table_name(COMPARISONS_TABLE).send().await {
.scan()
.table_name(COMPARISONS_TABLE)
.send()
.await
{
Ok(output) => { Ok(output) => {
if let Some(items) = output.items { if let Some(items) = output.items {
log!("Found comparisons from API: {:?}", items); log!("Found comparisons from API: {:?}", items);
@@ -475,16 +467,12 @@ pub async fn get_comparison_info() -> Result<Vec<ComparisonData>, ServerFnError>
} }
#[server] #[server]
pub async fn get_comparisons_categorized() -> Result<HashMap<String, Vec<ComparisonData>>, ServerFnError> { pub async fn get_comparisons_categorized()
-> Result<HashMap<String, Vec<ComparisonData>>, ServerFnError> {
let client = create_dynamo_client().await; let client = create_dynamo_client().await;
log!("Getting all comparisons from the database, by category."); log!("Getting all comparisons from the database, by category.");
match client match client.scan().table_name(COMPARISONS_TABLE).send().await {
.scan()
.table_name(COMPARISONS_TABLE)
.send()
.await
{
Ok(output) => { Ok(output) => {
if let Some(items) = output.items { if let Some(items) = output.items {
log!("Found comparisons, categorizing."); log!("Found comparisons, categorizing.");
@@ -492,14 +480,15 @@ pub async fn get_comparisons_categorized() -> Result<HashMap<String, Vec<Compari
let items: Vec<ComparisonData> = serde_dynamo::from_items(items)?; let items: Vec<ComparisonData> = serde_dynamo::from_items(items)?;
let categorized = items.iter().fold( let categorized = items.iter().fold(
HashMap::<String, Vec<ComparisonData>>::new(), |mut map, comp| { HashMap::<String, Vec<ComparisonData>>::new(),
|mut map, comp| {
// Load each comparison into a map, then return that map. // Load each comparison into a map, then return that map.
map.entry(comp.category.clone()) map.entry(comp.category.clone())
.and_modify(|vec| vec.push(comp.clone())) .and_modify(|vec| vec.push(comp.clone()))
.or_insert(vec![comp.clone()]); .or_insert(vec![comp.clone()]);
map map
} },
); );
Ok(categorized) Ok(categorized)
+23 -6
View File
@@ -7,7 +7,11 @@ mod imports {
pub use super::super::{MAIN_TABLE_NAME, S3_BUCKET_NAME}; pub use super::super::{MAIN_TABLE_NAME, S3_BUCKET_NAME};
pub use crate::pages::api::tokens::validate_and_get_token_info; pub use crate::pages::api::tokens::validate_and_get_token_info;
pub use crate::utils::server::create_aws_config; pub use crate::utils::server::create_aws_config;
pub use aws_sdk_dynamodb::types::{AttributeValue, ReturnValue, Select}; pub use aws_sdk_dynamodb::{
error::ProvideErrorMetadata as _,
types::{AttributeValue, ReturnValue, Select},
};
pub use aws_sdk_s3::error::ProvideErrorMetadata as _;
} }
#[server(input = MultipartFormData)] #[server(input = MultipartFormData)]
@@ -57,6 +61,9 @@ pub async fn upload_file(data: MultipartData) -> Result<String, ServerFnError> {
if input_name.is_empty() { if input_name.is_empty() {
return Err(ServerFnError::new("Missing input file")); return Err(ServerFnError::new("Missing input file"));
} }
if file_bytes.is_empty() {
return Err(ServerFnError::new("Missing file contents"));
}
let user_claims = validate_and_get_token_info(access_token).await?; let user_claims = validate_and_get_token_info(access_token).await?;
let subject = user_claims.subject; let subject = user_claims.subject;
@@ -75,7 +82,12 @@ pub async fn upload_file(data: MultipartData) -> Result<String, ServerFnError> {
.send() .send()
.await .await
.map(|_| ()) .map(|_| ())
.map_err(|e| ServerFnError::new(format!("Couldn't put file to S3: {}", e.to_string())))?; .map_err(|e| {
ServerFnError::new(format!(
"Couldn't put file to S3: {}",
e.message().unwrap_or_default()
))
})?;
// Besides uploading the file to S3, we also want to store a FILE entry in Dynamo. // Besides uploading the file to S3, we also want to store a FILE entry in Dynamo.
// This will allow the server to quickly return a list of all files that a specific user has // This will allow the server to quickly return a list of all files that a specific user has
@@ -104,7 +116,7 @@ pub async fn upload_file(data: MultipartData) -> Result<String, ServerFnError> {
.map_err(|e| { .map_err(|e| {
ServerFnError::new(format!( ServerFnError::new(format!(
"Couldn't put file to Dynamo, file rolled back: {}", "Couldn't put file to Dynamo, file rolled back: {}",
e.to_string() e.message().unwrap_or_default()
)) ))
}); });
@@ -119,7 +131,7 @@ pub async fn upload_file(data: MultipartData) -> Result<String, ServerFnError> {
.map_err(|e| { .map_err(|e| {
ServerFnError::new(format!( ServerFnError::new(format!(
"Couldn't roll back S3 operation: {}", "Couldn't roll back S3 operation: {}",
e.to_string() e.message().unwrap_or_default()
)) ))
})?; })?;
@@ -180,7 +192,7 @@ pub async fn delete_file(
.map_err(|err| { .map_err(|err| {
ServerFnError::new(format!( ServerFnError::new(format!(
"Couldn't roll back Dynamo operation: {}", "Couldn't roll back Dynamo operation: {}",
err.to_string() err.message().unwrap_or_default()
)) ))
})?; })?;
@@ -230,5 +242,10 @@ pub async fn list_files(
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
}) })
.map_err(|e| ServerFnError::new(format!("Couldn't list files: {}", e.to_string()))) .map_err(|e| {
ServerFnError::new(format!(
"Couldn't list files: {}",
e.message().unwrap_or_default()
))
})
} }
+2 -1
View File
@@ -40,7 +40,8 @@ pub fn StudentAthleticsPage() -> impl IntoView {
"Lacrosse", "Lacrosse",
"Softball", "Softball",
"Tennis", "Tennis",
"Baseball" "Baseball",
"Volleyball"
] ]
), ),
input!( input!(