feat: begin implementation of student data

This commit is contained in:
alphafetus committed 2026-02-04 13:59:20 -05:00
1 parent d7c4c6b707
commit 5a064bdd69
7 files changed
+201 -14

No files matched your search

+4
View File
@@ -3,6 +3,7 @@ mod scholarships;
mod dates;
mod admin;
mod providers;
pub mod students;
pub use comparisons::*;
pub use scholarships::*;
@@ -21,3 +22,6 @@ pub static SCHOLARSHIPS_TABLE: &str = "leptos-scholarships";
#[cfg(feature = "ssr")]
pub static PROVIDER_CONTACT_TABLE: &str = "leptos-provider-contacts";
#[cfg(feature = "ssr")]
pub static MAIN_TABLE_NAME: &str = "scholarships-main";
+77
View File
@@ -0,0 +1,77 @@
use leptos::prelude::*;
#[server]
pub async fn put_student_data(
subject: String,
data_type: String,
data_map: std::collections::HashMap<String, crate::common::ValueType>
) -> Result<(), ServerFnError> {
use leptos::logging::log;
use crate::utils::server::{create_dynamo_client, into_attr_map};
use aws_sdk_dynamodb::types::AttributeValue;
use super::MAIN_TABLE_NAME;
let client = create_dynamo_client().await;
let mut data_map_attr = into_attr_map(data_map);
data_map_attr.insert(
"HK".into(),
AttributeValue::S(format!("STUDENT#{}", subject))
);
data_map_attr.insert(
"SK".into(),
AttributeValue::S(data_type.to_uppercase())
);
log!("Inserting this item: {:?}", data_map_attr);
client
.put_item()
.table_name(MAIN_TABLE_NAME)
.set_item(Some(data_map_attr))
.send()
.await
.map(|_| ())
.map_err(ServerFnError::from)
}
#[server]
pub async fn get_student_data(
subject: String,
data_type: String,
) -> Result<std::collections::HashMap<String, crate::common::ValueType>, ServerFnError> {
use crate::utils::server::create_dynamo_client;
use std::collections::HashMap;
use crate::common::ValueType;
use aws_sdk_dynamodb::types::AttributeValue;
use super::MAIN_TABLE_NAME;
let client = create_dynamo_client().await;
client
.query()
.table_name(MAIN_TABLE_NAME)
.key_condition_expression("HK = :hk AND SK = :sk")
.expression_attribute_values(":hk", AttributeValue::S(format!("STUDENT#{}", subject)))
.expression_attribute_values(":sk", AttributeValue::S(data_type.to_uppercase()))
.send()
.await
.map(|output| {
// Map the output to a HashMap<String, ValueType>
let Some(list) = output.items else {
return HashMap::new();
};
let mut map = HashMap::new();
let _ = list
.into_iter()
.flatten()
.for_each(|(k, v)| {
map.insert(k, ValueType::from(&v));
});
map
})
.map_err(ServerFnError::from)
}
+9 -9
View File
@@ -1,18 +1,18 @@
use std::collections::HashMap;
use leptos::prelude::*;
use leptos::prelude::*;
use crate::components::{OutlinedTextField, TextFieldType, ValidatedForm};
use crate::pages::student::form_setup::use_student_form;
#[component]
pub fn StudentAcademicsPage() -> impl IntoView {
let data_map = RwSignal::new(HashMap::new());
let controller = use_student_form("academics");
view! {
<div class="flex flex-1" />
<div class="flex flex-col flex-2 mt-6">
<ValidatedForm title="Academic Information" on_submit=move || {}>
<ValidatedForm title="Academic Information" on_submit=controller.submit_action>
<OutlinedTextField
label="Unweighted GPA:"
data_map=data_map
data_map=controller.data_map
data_member="unweighted_gpa"
placeholder="4.0"
required=true
@@ -20,7 +20,7 @@ pub fn StudentAcademicsPage() -> impl IntoView {
/>
<OutlinedTextField
label="Weighted GPA:"
data_map=data_map
data_map=controller.data_map
data_member="weighted_gpa"
placeholder="4.5"
required=true
@@ -28,7 +28,7 @@ pub fn StudentAcademicsPage() -> impl IntoView {
/>
<OutlinedTextField
label="Highest SAT Score:"
data_map=data_map
data_map=controller.data_map
data_member="sat_score"
placeholder="1600"
required=false
@@ -36,7 +36,7 @@ pub fn StudentAcademicsPage() -> impl IntoView {
/>
<OutlinedTextField
label="Highest ACT Score:"
data_map=data_map
data_map=controller.data_map
data_member="act_score"
placeholder="36"
required=false
@@ -44,7 +44,7 @@ pub fn StudentAcademicsPage() -> impl IntoView {
/>
<OutlinedTextField
label="Academic Honors:"
data_map=data_map
data_map=controller.data_map
data_member="academic_honors"
placeholder="TBD"
required=false
+5 -5
View File
@@ -1,20 +1,20 @@
use std::collections::HashMap;
use leptos::prelude::*;
use leptos::prelude::*;
use crate::components::{MultiEntry, ValidatedForm};
use crate::input;
use crate::pages::student::form_setup::use_student_form;
#[component]
pub fn StudentAthleticsPage() -> impl IntoView {
let data_map = RwSignal::new(HashMap::new());
let controller = use_student_form("athletics");
view! {
<div class="flex flex-1" />
<div class="flex flex-col flex-2 mt-6">
<ValidatedForm title="Athletics Information" on_submit=move || {}>
<ValidatedForm title="Athletics Information" on_submit=controller.submit_action>
<MultiEntry
label="Athletics Activities:"
description="Leave blank if not applicable."
data_map=data_map
data_map=controller.data_map
data_member="athletic_participation"
schema=vec![
input!(
+63
View File
@@ -0,0 +1,63 @@
use std::collections::HashMap;
use leptos::prelude::*;
use crate::common::ValueType;
use crate::pages::api::students::{get_student_data, PutStudentData};
use crate::utils::get_user_claims;
pub struct StudentFormInfo {
pub data_map: RwSignal<HashMap<String, ValueType>>,
pub user_id: Memo<Option<String>>,
pub submit_action: Callback<()>,
pub refresh_trigger: Trigger,
pub data_resource: Resource<Result<HashMap<String, ValueType>, ServerFnError>>
}
pub fn use_student_form(
form_type: impl Into<Signal<String>>
) -> StudentFormInfo {
let form_type = form_type.into();
let user_claims = get_user_claims();
let user_id = Memo::new(move |_| {
user_claims.get()
.map(|info| info.claims.subject.clone())
});
let data_map = RwSignal::new(HashMap::new());
let refresh_trigger = Trigger::new();
let data_resource = Resource::new(
move || (user_id.get().unwrap_or_default(), form_type.get(), refresh_trigger.track()),
async move |(id, form_type, _)| get_student_data(
id,
form_type,
).await
);
Effect::new(move || {
data_resource.with(|map_opt| {
let Some(Ok(map)) = map_opt else {
return;
};
data_map.set(map.to_owned());
});
});
let submit_action = ServerAction::<PutStudentData>::new();
let on_submit = move || {
submit_action.dispatch(PutStudentData {
subject: user_id.get().unwrap_or_default(),
data_type: form_type.get(),
data_map: data_map.get()
});
};
StudentFormInfo {
data_map,
refresh_trigger,
data_resource,
user_id,
submit_action: on_submit.into(),
}
}
+42
View File
@@ -1,13 +1,55 @@
use std::collections::HashMap;
use leptos::logging::log;
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="text-lg">
"This is just a placeholder for now. We'll show some redirect buttons."
</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>
}
}
+1
View File
@@ -8,6 +8,7 @@ mod extracurriculars_page;
mod work_experience_page;
mod university_page;
mod family_info_page;
mod form_setup;
pub use shell::*;
pub use home_page::*;