Release candidate (v0.5.0) #65

Merged
BobdaFett merged 13 commits from release/v0.5.0 into main 2026-03-06 12:39:50 +00:00
17 changed files with 294 additions and 96 deletions

No files matched your search

+5 -2
View File
@@ -1,11 +1,11 @@
use crate::components::ToastList;
use crate::components::login::ProviderLoginContext;
use crate::pages::{student, FormClosedPage};
use crate::pages::{
AdminHomePage, AdminProviderPage, AdminScholarshipPage, AdminShell, AdminUtilsPage,
ApplicantsPageFallback, AuthCallbackPage, LoanerBorrowForm, LoanerFallback, LoanerReturnForm,
LoanerShell, ProviderContactPage, ProviderPortal,
};
use crate::pages::{FormClosedPage, student};
use leptos::prelude::*;
use leptos_meta::{MetaTags, Stylesheet, Title, provide_meta_context};
use leptos_oidc::AuthSignal;
@@ -137,7 +137,6 @@ fn StudentRoutes() -> impl MatchNestedRoutes + Clone {
<Route path=path!("demographics") view=student::StudentDemographicsPage />
<Route path=path!("eligibility") view=student::StudentEligibilityPage />
<Route path=path!("additional") view=student::AdditionalPage />
<Route path=path!("testing") view=student::StudentTestDisplay />
<Route path=path!("additional/academics") view=student::StudentAcademicsPage />
<Route path=path!("additional/athletics") view=student::StudentAthleticsPage />
<Route
@@ -151,6 +150,10 @@ fn StudentRoutes() -> impl MatchNestedRoutes + Clone {
<Route path=path!("additional/university") view=student::StudentUniversityPage />
<Route path=path!("additional/family-info") view=student::StudentFamilyPage />
<Route path=path!("additional/financials") view=student::StudentFinancialPage />
<Route
path=path!("additional/specifics")
view=student::StudentSpecificRequirementsPage
/>
</ParentRoute>
}
.into_inner()
+9 -5
View File
@@ -27,7 +27,7 @@ pub fn DashboardButton(
#[prop(into)]
description: String,
/// The file path or URL of the icon displayed at the top of the button.
#[prop(into)]
#[prop(optional, into)]
icon: String,
/// The route that the user is navigated to when clicking the button.
#[prop(into)]
@@ -37,6 +37,8 @@ pub fn DashboardButton(
let on_click = move |_| navigate(&path, Default::default());
let icon = StoredValue::new(icon);
view! {
<button
type="button"
@@ -45,14 +47,16 @@ pub fn DashboardButton(
shadow-[inset_0_0_6px_rgba(0,0,0,0.12)]"
on:click=on_click
>
<div class="flex flex-col">
<img src=icon.clone() class="h-8 w-8" alt="icon" />
<h3 class="font-semibold text-base">{title.clone()}</h3>
<Show when=move || !icon.get_value().is_empty()>
<img src=icon.get_value() class="h-8 w-8" alt="icon" />
</Show>
<h3 class="font-semibold text-base">{title.clone()}</h3>
<p class="text-sm text-gray-600 pt-6">{description.clone()}</p>
</div>
</button>
}.into_any()
}
.into_any()
}
+7 -8
View File
@@ -1,5 +1,5 @@
use leptos::prelude::*;
use crate::components::Banner;
use crate::components::Banner;
use leptos::prelude::*;
#[component]
pub fn FormClosedPage() -> impl IntoView {
@@ -7,14 +7,13 @@ pub fn FormClosedPage() -> impl IntoView {
// about which form is closed right now.
view! {
<Banner
title="R15 Scholarships"
logo="/PHS_Stacked_Acronym.png"
path="/providers"
/>
<Banner title="R15 Scholarships" logo="/PHS_Stacked_Acronym.png" path="/providers" />
<div class="flex flex-1 flex-col gap-4 items-center m-4">
<div class="font-bold text-3xl">"This form has been closed!"</div>
<div class="text-lg">"If you believe this is an error, please contact Sara Smith at "<a href="mailto:ssmith@region15.org">"ssmith@region15.org"</a></div>
<div class="text-lg">
"If you believe this is an error, please contact Sara Smith at "
<a href="mailto:ssmith@region15.org">"ssmith@region15.org"</a>
</div>
</div>
}
}
+2 -1
View File
@@ -18,6 +18,7 @@ pub fn StudentAcademicsPage() -> impl IntoView {
<div class="flex flex-col flex-2 mt-6">
<ValidatedForm
title="Academic Information"
description="This information will allow you to be eligible for any scholarships that have specific academic/grade requirements."
on_submit=controller.submit_action
>
<OutlinedTextField
@@ -56,7 +57,7 @@ pub fn StudentAcademicsPage() -> impl IntoView {
label="Academic Honors:"
data_map=controller.data_map
data_member="academic_honors"
placeholder="TBD"
placeholder="High Honors, etc."
required=false
input_type=TextFieldType::Text
/>
+55 -3
View File
@@ -1,13 +1,65 @@
use leptos::prelude::*;
use crate::components::DashboardButton;
#[component]
pub fn AdditionalPage() -> impl IntoView {
view! {
<div class="flex flex-1 flex-col gap-4 items-center m-4">
<div class="font-bold text-3xl">"Student Additional Forms"</div>
<div class="flex-1" />
<div class="flex flex-2 flex-col gap-4 items-center m-4">
<div class="font-bold text-3xl">"Student Additional Information Forms"</div>
<div class="text-lg">
"This is just a placeholder for now. We'll show redirect buttons to each form."
"All forms listed under this tab are for additional eligibility factors that some scholarships require."
</div>
<div class="text-lg">
"None of these forms are required, but they may increase the total number of scholarships that you are eligible to be applied to."
</div>
<DashboardButton
title="Scholarship Book"
description="View scholarship requirements"
icon="/Book.png"
path="https://docs.google.com/document/d/1qVs0Lwvwl1bVGHznocoev3AF56KlhTfs/view"
/>
<DashboardButton
title="Academic Information"
description="Grades, tests, and more"
path="/students/additional/academics"
/>
<DashboardButton
title="Athletics"
description="Sports and outdoors"
path="/students/additional/athletics"
/>
<DashboardButton
title="Work Experience"
description="Add job information"
path="/students/additional/work-experience"
/>
<DashboardButton
title="Extracurriculars"
description="Clubs and activities"
path="/students/additional/extracurriculars"
/>
<DashboardButton
title="University Information"
description="Where are you going to school?"
path="/students/additional/university"
/>
<DashboardButton
title="Family Information"
description="General parent and sibling information"
path="/students/additional/family-info"
/>
<DashboardButton
title="Scholarship Specifics"
description="Scholarship-specific questions"
path="/students/additional/specifics"
/>
<DashboardButton
title="Financials"
description="FAFSA SAR Upload"
path="/students/additional/financials"
/>
</div>
<div class="flex-1" />
}
}
+7 -3
View File
@@ -1,7 +1,7 @@
use leptos::prelude::*;
use crate::components::{Loading, MultiEntry, ValidatedForm};
use crate::components::{Loading, MultiEntry, ValidatedForm};
use crate::input;
use crate::pages::student::form_setup::use_student_form;
use leptos::prelude::*;
#[component]
pub fn StudentAthleticsPage() -> impl IntoView {
@@ -11,7 +11,11 @@ pub fn StudentAthleticsPage() -> impl IntoView {
<Show when=move || controller.data_resource.get().is_some() fallback=Loading>
<div class="flex flex-1" />
<div class="flex flex-col flex-2 mt-6">
<ValidatedForm title="Athletics Information" on_submit=controller.submit_action>
<ValidatedForm
title="Athletics Information"
description="This information will allow you to be eligible for any scholarships that have specific sports requirements."
on_submit=controller.submit_action
>
<MultiEntry
label="Athletics Activities:"
description="Leave blank if not applicable."
+25 -4
View File
@@ -1,6 +1,6 @@
use leptos::prelude::*;
use crate::components::{Loading, OutlinedTextField, Select, TextFieldType, ValidatedForm};
use crate::pages::student::form_setup::use_student_form;
use leptos::prelude::*;
#[component]
pub fn StudentDemographicsPage() -> impl IntoView {
@@ -15,8 +15,11 @@ pub fn StudentDemographicsPage() -> impl IntoView {
<Show when=move || controller.data_resource.get().is_some() fallback=Loading>
<div class="flex flex-1" />
<div class="flex flex-col flex-2 mt-6">
<ValidatedForm title="Student Demographic Form" on_submit=controller.submit_action>
// Inputs here.
<ValidatedForm
title="Student Demographic Form"
description="Here you'll set up some basic information about yourself."
on_submit=controller.submit_action
>
<OutlinedTextField
label="First Name:"
data_map=controller.data_map
@@ -33,7 +36,25 @@ pub fn StudentDemographicsPage() -> impl IntoView {
input_type=TextFieldType::Text
required=true
/>
// There must be a date of birth selection field here.
<OutlinedTextField
label="Date of Birth:"
data_map=controller.data_map
data_member="dob"
placeholder="MM/DD/YYYY"
input_type=TextFieldType::Text
required=true
/>
<Select
label="Gender:"
data_map=controller.data_map
data_member="dob"
required=true
value_list=vec![
"Male".to_string(),
"Female".to_string(),
"Prefer not to answer/Other".to_string(),
]
/>
<OutlinedTextField
label="Student ID Number:"
data_map=controller.data_map
+2 -4
View File
@@ -1,5 +1,5 @@
use crate::common::{ComparisonData, ExpandableInfo, ValueType};
use crate::components::{FileDrop, Header};
use crate::components::{FileDrop, Header, Loading};
use crate::pages::api::files::list_files;
use crate::pages::api::students::get_all_student_data;
use crate::pages::api::{get_all_scholarship_info, get_comparison_info};
@@ -44,9 +44,7 @@ pub fn StudentEligibilityPage() -> impl IntoView {
view! {
<div class="flex-1" />
<div class="flex-2 mb-4 flex flex-col">
<Suspense fallback=move || {
view! { <div>"Checking eligibility..."</div> }
}>
<Suspense fallback=Loading>
{move || {
let Some(Ok(scholarships_list)) = scholarships_resource.get() else {
return view! {}.into_any();
+2 -1
View File
@@ -13,6 +13,7 @@ pub fn StudentExtracurricularsPage() -> impl IntoView {
<div class="flex flex-col flex-2 mt-6">
<ValidatedForm
title="Extracurricular Information"
description="This information will allow you to be eligible for any scholarships that have specific extracurricular requirements."
on_submit=controller.submit_action
>
// Inputs here.
@@ -36,7 +37,7 @@ pub fn StudentExtracurricularsPage() -> impl IntoView {
input!(Number, "num_hours", "Number of hours completed:", true, "40"),
input!(Number, "num_weeks", "Number of weeks participated:", true, "3"),
input!(
Text, "special_involvement", "Any special involvement:", false, ""
Text, "special_involvement", "Any special involvement:", false, "Club president, etc."
),
input!(
Checkbox, "grades", "Grades Participated:", true, ["9th", "10th", "11th", "12th"]
+5 -3
View File
@@ -10,7 +10,11 @@ pub fn StudentFamilyPage() -> impl IntoView {
<Show when=move || controller.data_resource.get().is_some() fallback=Loading>
<div class="flex flex-1" />
<div class="flex flex-col flex-2 mt-6">
<ValidatedForm title="Family Information" on_submit=controller.submit_action>
<ValidatedForm
title="Family Information"
description="General family information. You must fill out information about at least one parent."
on_submit=controller.submit_action
>
// Inputs here.
<OutlinedTextField
label="Total number of children in family:"
@@ -71,7 +75,6 @@ pub fn StudentFamilyPage() -> impl IntoView {
input_type=TextFieldType::Text
required=false
/>
// TODO Could this be a select dropdown?
<OutlinedTextField
label="Parent/Guardian 2 Relationship:"
data_map=controller.data_map
@@ -80,7 +83,6 @@ pub fn StudentFamilyPage() -> impl IntoView {
input_type=TextFieldType::Text
required=false
/>
// TODO Get a better example
<OutlinedTextField
label="Parent/Guardian 2 Occupation:"
data_map=controller.data_map
+2 -3
View File
@@ -28,10 +28,10 @@ pub fn StudentFinancialPage() -> impl IntoView {
view! {
<div class="flex flex-1" />
<div class="flex flex-col flex-2 mt-6">
// <ValidatedForm title="Student Demographic Form" on_submit=controller.submit_action>
<Header
title="Financial Information"
description="Please upload your FAFSA SAR (Student Aid Report) here."
description="Please upload your FAFSA SAR (Student Aid Report) here. This information is NOT required,
but some scholarships may recognize this as a requirement."
/>
<Suspense fallback=Loading>
<FileDrop
@@ -41,7 +41,6 @@ pub fn StudentFinancialPage() -> impl IntoView {
allowed_types=vec![".pdf".to_string()]
/>
</Suspense>
// </ValidatedForm>
</div>
<div class="flex flex-1" />
}
+28 -45
View File
@@ -1,55 +1,38 @@
use std::collections::HashMap;
use leptos::logging::log;
use crate::components::DashboardButton;
use leptos::prelude::*;
use crate::common::ValueType;
use crate::components::ActionButton;
use crate::pages::api::students::{GetStudentData, PutStudentData};
use crate::utils::get_user_claims;
#[component]
pub fn StudentHomePage() -> impl IntoView {
let user_claims = get_user_claims();
let user_id = Memo::new(move |_| {
user_claims.get()
.map(|info| info.claims.subject.clone())
});
let create_student_data = ServerAction::<PutStudentData>::new();
let get_student_data = ServerAction::<GetStudentData>::new();
let on_click_student_test = move |_| {
create_student_data.dispatch(PutStudentData {
subject: user_id.get().unwrap_or_default(),
data_type: "TESTING".into(),
data_map: HashMap::from([
("testing_info".into(), ValueType::String(Some("hello!".to_string())))
]),
});
};
let on_click_get_student = move |_| {
get_student_data.dispatch(GetStudentData {
subject: user_id.get().unwrap_or_default(),
data_type: "TESTING".into()
});
};
Effect::new(move || {
let Some(value) = get_student_data.value().get() else {
return;
};
log!("{:?}", value);
});
view! {
<div class="flex flex-1 flex-col gap-4 items-center m-4">
<div class="font-bold text-3xl">"Student Home Page"</div>
<div class="flex-1" />
<div class="flex flex-2 flex-col gap-4 items-center m-4 mt-10">
<div class="font-bold text-3xl">"Region 15 Scholarship Application"</div>
<div class="text-lg">
"This is just a placeholder for now. We'll show some redirect buttons."
"Welcome to the R15 general scholarship application! If you have any questions, please direct them to Sara Smith at "
<a class="text-blue-500 underline" href="mailto:ssmith@region15.org">
"ssmith@region15.org"
</a>"."
</div>
<ActionButton on:click=on_click_student_test>"Test Put Student Function"</ActionButton>
<ActionButton on:click=on_click_get_student>"Test Get Student Function"</ActionButton>
<div class="text-lg">"Here are some links to get you started:"</div>
<DashboardButton
title="Scholarship Book"
description="View scholarship requirements"
icon="/Book.png"
path="https://docs.google.com/document/d/1qVs0Lwvwl1bVGHznocoev3AF56KlhTfs/view"
/>
<DashboardButton
title="Demographics Form"
description="Basic student information"
icon="/Person_Black.png"
path="/students/demographics"
/>
<DashboardButton
title="Additional Forms"
description="Extra eligibility factors"
icon="/Form_Black.png"
path="/students/additional"
/>
</div>
<div class="flex-1" />
}
}
+2
View File
@@ -12,6 +12,7 @@ mod shell;
mod test_display;
mod university_page;
mod work_experience_page;
mod specific_requirements_page;
pub use academics_page::*;
pub use additional_page::*;
@@ -26,3 +27,4 @@ pub use shell::*;
pub use test_display::*;
pub use university_page::*;
pub use work_experience_page::*;
pub use specific_requirements_page::*;
+1 -1
View File
@@ -35,12 +35,12 @@ pub fn StudentShell() -> impl IntoView {
SubTabInfo::new("Extracurriculars", "extracurriculars"),
SubTabInfo::new("University Info", "university"),
SubTabInfo::new("Family Info", "family-info"),
SubTabInfo::new("Specifics", "specifics"),
SubTabInfo::new("Financials", "financials"),
],
),
),
TabInfo::new("Eligible Scholarships", "eligibility", None),
TabInfo::new("Testing Display", "testing", None)
]
/>
</Authenticated>
@@ -0,0 +1,83 @@
use crate::components::{Loading, RadioList, ValidatedForm};
use crate::pages::student::form_setup::use_student_form;
use leptos::prelude::*;
#[component]
pub fn StudentSpecificRequirementsPage() -> impl IntoView {
let controller = use_student_form("specifics");
view! {
<Suspense fallback=Loading>
{move || {
controller
.data_resource
.get()
.map(move |_| {
view! {
<div class="flex-1" />
<div class="flex flex-col flex-2 mt-6">
<ValidatedForm
title="Scholarship-Specific Eligibility Requirements"
description="These questions are specific to some scholarships. Indicate yes or no for each question."
on_submit=controller.submit_action
>
<RadioList
label="Have you attended BAS?"
data_map=controller.data_map
data_member="attend_bas"
items=vec!["Yes".to_string(), "No".to_string()]
required=true
/>
<RadioList
label="Are you a member of Midd-South Catholic Church?"
data_map=controller.data_map
data_member="middsouth_church"
items=vec!["Yes".to_string(), "No".to_string()]
required=true
/>
<RadioList
label="Do you have a family member that is or has served in the US military? (Note: you may be asked for proof of service)"
data_map=controller.data_map
data_member="family_military_service"
items=vec!["Yes".to_string(), "No".to_string()]
required=true
/>
<RadioList
label="Have you participated in Pomperaug Youth Baseball?"
data_map=controller.data_map
data_member="youth_baseball"
items=vec!["Yes".to_string(), "No".to_string()]
required=true
/>
<RadioList
label="Have you participated in the Panthers Aquatic Club?"
data_map=controller.data_map
data_member="aquatic_club"
items=vec!["Yes".to_string(), "No".to_string()]
required=true
/>
<RadioList
label="Are at least one of your parents a member of the Region 15 PEA?"
data_map=controller.data_map
data_member="pea_member"
items=vec!["Yes".to_string(), "No".to_string()]
required=true
/>
<RadioList
label="Have you participated in the PHS Music Program (Band, Chorus, etc)?"
data_map=controller.data_map
data_member="music_program"
items=vec!["Yes".to_string(), "No".to_string()]
required=true
/>
</ValidatedForm>
</div>
<div class="flex-1" />
}
.into_any()
})
.collect_view()
}}
</Suspense>
}
}
+43 -7
View File
@@ -13,7 +13,11 @@ pub fn StudentUniversityPage() -> impl IntoView {
<Show when=move || controller.data_resource.get().is_some() fallback=Loading>
<div class="flex flex-1" />
<div class="flex flex-col flex-2 mt-6">
<ValidatedForm title="University Information" on_submit=controller.submit_action>
<ValidatedForm
title="University Information"
description="Here you may fill out general information about the university you plan to attend."
on_submit=controller.submit_action
>
// Inputs here.
<OutlinedTextField
label="University Name:"
@@ -31,12 +35,11 @@ pub fn StudentUniversityPage() -> impl IntoView {
input_type=TextFieldType::Text
required=true
/>
// TODO Make this a select dropdown.
<OutlinedTextField
label="University State:"
data_map=controller.data_map
data_member="college_state"
placeholder="The State of Panic"
placeholder="CT, AZ, etc."
input_type=TextFieldType::Text
required=true
/>
@@ -55,14 +58,47 @@ pub fn StudentUniversityPage() -> impl IntoView {
required=true
value_list=vec!["Yes".to_string(), "No".to_string()]
/>
// TODO Make this a select dropdown.
<OutlinedTextField
<Select
label="Chosen Major:"
data_map=controller.data_map
data_member="major"
placeholder="Computer Science"
input_type=TextFieldType::Text
required=true
value_list=vec![
"Music".to_string(),
"Education".to_string(),
"Special Education".to_string(),
"Speech Pathology".to_string(),
"School Psychology".to_string(),
"School Counseling".to_string(),
"Occupational Therapy".to_string(),
"Physical Therapy".to_string(),
"Nursing".to_string(),
"Allied Health".to_string(),
"Fine/Performing Arts".to_string(),
"Writing/Communication".to_string(),
"History".to_string(),
"Government".to_string(),
"Political Science".to_string(),
"Social Work".to_string(),
"Sports Medicine".to_string(),
"Athletic Training".to_string(),
"Horticulture".to_string(),
"Conservation Studies".to_string(),
"Ecology".to_string(),
"Environmental Studies".to_string(),
"Urban Planning".to_string(),
"Landscaping".to_string(),
"Legal Studies".to_string(),
"Criminal Justice".to_string(),
]
/>
<OutlinedTextField
label="Intended Career:"
data_map=controller.data_map
data_member="intended_career"
placeholder="Engineer, Artist, etc."
input_type=TextFieldType::Text
required=false
/>
</ValidatedForm>
</div>
+16 -6
View File
@@ -1,7 +1,7 @@
use leptos::prelude::*;
use crate::components::{Loading, MultiEntry, ValidatedForm};
use crate::components::{Loading, MultiEntry, ValidatedForm};
use crate::input;
use crate::pages::student::form_setup::use_student_form;
use leptos::prelude::*;
#[component]
pub fn StudentWorkExperiencePage() -> impl IntoView {
@@ -11,7 +11,11 @@ pub fn StudentWorkExperiencePage() -> impl IntoView {
<Show when=move || controller.data_resource.get().is_some() fallback=Loading>
<div class="flex flex-1" />
<div class="flex flex-col flex-2 mt-6">
<ValidatedForm title="Student Work Experience" on_submit=controller.submit_action>
<ValidatedForm
title="Student Work Experience"
description="Here you may add information about your past/current workplace."
on_submit=controller.submit_action
>
<MultiEntry
label="Work Experience:"
description="Leave blank if not applicable."
@@ -22,9 +26,15 @@ pub fn StudentWorkExperiencePage() -> impl IntoView {
Text, "job_title", "Job Title:", true, "Waiter, Cashier, etc..."
),
input!(Text, "employer", "Employer:", true, "Your employer's name..."),
input!(Number, "start_date", "Start date:", true, "40"),
input!(Number, "end_date", "End date:", true, "3"),
input!(Number, "num_hours", "Number of hours completed:", true, "120"),
input!(
Text, "start_date", "Approximate start date:", true, "01/01/2000"
),
input!(
Text, "end_date", "End date:", true, "01/01/2026, or current if not applicable"
),
input!(
Number, "num_hours", "Approximate number of hours per week:", true, "25"
),
]
/>
</ValidatedForm>