Merge pull request #34 from R15-Software-Dev/feat/provider_portal

Provider Dashboard
This commit was merged in pull request #34.
This commit is contained in:
Morrighan authored and GitHub committed 2025-12-22 07:36:00 -05:00
commit c8be9a35cf
15 files changed
+256 -170

No files matched your search

+11 -5
View File
@@ -1,9 +1,12 @@
use leptos::logging::log;
use crate::pages::{AboutPage, ComparisonTestPage, HomePage, LoanerPage, ProviderPortal, TestPage};
use leptos::leptos_dom::log;
use leptos::prelude::*;
use leptos_meta::{MetaTags, Stylesheet, Title, provide_meta_context};
use leptos_oidc::{Auth, AuthParameters, AuthSignal, Challenge};
use leptos_router::{components::{Route, Router, Routes}, path};
use crate::pages::{AboutPage, ComparisonTestPage, HomePage, LoanerPage, TestPage};
use leptos_router::{
components::{Route, Router, Routes},
path,
};
pub fn shell(options: LeptosOptions) -> impl IntoView {
view! {
@@ -44,7 +47,8 @@ pub fn App() -> impl IntoView {
}
fn use_origin() -> String {
#[cfg(target_arch = "wasm32")] {
#[cfg(target_arch = "wasm32")]
{
use leptos::web_sys;
// Just get the current URL origin.
web_sys::window()
@@ -54,7 +58,8 @@ fn use_origin() -> String {
.unwrap_or("http://localhost:3000/".to_string())
}
#[cfg(not(target_arch = "wasm32"))] {
#[cfg(not(target_arch = "wasm32"))]
{
// Read the expected origin out of an environment variable.
std::env::var("LP_SITE_ORIGIN").unwrap_or("http://localhost:3000/".to_string())
}
@@ -104,6 +109,7 @@ pub fn AppWithRoutes() -> impl IntoView {
<Route path=path!("about") view=AboutPage/>
<Route path=path!("test_page") view=TestPage/>
<Route path=path!("comparison") view=ComparisonTestPage />
<Route path=path!("providers") view=ProviderPortal />
<Route path=path!("loaners") view=LoanerPage />
<Route path=path!("loaners/:form_name") view=LoanerPage />
</Routes>
+8 -3
View File
@@ -42,8 +42,13 @@ impl ComparisonData {
}
pub fn compare(&self, student_data: &ExpandableInfo) -> Result<bool, String> {
let value = student_data.data.get(&self.member)
.expect(format!("Couldn't find member {:?}, current student data: {:?}", self.member, student_data).as_str());
let value = student_data.data.get(&self.member).expect(
format!(
"Couldn't find member {:?}, current student data: {:?}",
self.member, student_data
)
.as_str(),
);
self.comparison.evaluate(value, &self.target_value)
}
}
@@ -73,8 +78,8 @@ impl Comparison for ComparisonType {
#[cfg(test)]
mod tests {
use crate::common::comparison::test_utils::create_student_data;
use super::*;
use crate::common::comparison::test_utils::create_student_data;
#[test]
fn test_student_data() {
+6 -6
View File
@@ -288,10 +288,12 @@ impl Comparison for MapListComparison {
#[cfg(test)]
mod tests {
use super::super::test_utils::create_map_list_helper;
use super::*;
use super::super::test_utils::{create_map_list_helper};
use crate::common::{ComparisonType};
use crate::common::comparison::test_utils::{create_empty_lists, create_number_list, create_text_list};
use crate::common::ComparisonType;
use crate::common::comparison::test_utils::{
create_empty_lists, create_number_list, create_text_list,
};
#[test]
fn map_list_flatten_to_text_list() {
@@ -441,9 +443,7 @@ mod tests {
fn text_list_empty() {
let none_list = ValueType::List(None);
let empty_list = ValueType::List(Some(vec![]));
let long_list = ValueType::List(Some(vec![
ValueType::String(None),
]));
let long_list = ValueType::List(Some(vec![ValueType::String(None)]));
let empty_comp = ComparisonType::TextList(TextListComparison::IsEmpty);
let not_empty_comp = ComparisonType::TextList(TextListComparison::IsNotEmpty);
+16 -17
View File
@@ -9,8 +9,8 @@ pub use self::{base::*, lists::*, number::*, text::*, traits::*};
#[cfg(test)]
mod test_utils {
use std::collections::HashMap;
use crate::common::{ExpandableInfo, ValueType};
use std::collections::HashMap;
pub fn create_student_data() -> ExpandableInfo {
let map = create_map_helper(
@@ -40,19 +40,16 @@ mod test_utils {
ValueType::String(Some(10.to_string())),
])),
ValueType::String(Some("N/A".to_string())),
]
],
))),
ValueType::Map(Some(create_map_helper(
vec![
"activity_name".to_string(),
"total_hours".to_string(),
],
vec!["activity_name".to_string(), "total_hours".to_string()],
vec![
ValueType::String(Some("Raking".to_string())),
ValueType::Number(Some(30.to_string())),
]
],
))),
]
],
);
let mut info = ExpandableInfo::new("test");
@@ -61,9 +58,11 @@ mod test_utils {
info
}
pub fn create_map_helper(keys: Vec<String>, values: Vec<ValueType>) -> HashMap<String, ValueType> {
keys
.iter()
pub fn create_map_helper(
keys: Vec<String>,
values: Vec<ValueType>,
) -> HashMap<String, ValueType> {
keys.iter()
.cloned()
.zip(values.iter().cloned())
.collect::<HashMap<String, ValueType>>()
@@ -76,7 +75,7 @@ mod test_utils {
"last_name".to_string(),
"gender".to_string(),
"sat_score".to_string(),
"grades_participated".to_string()
"grades_participated".to_string(),
],
vec![
ValueType::String(Some("John".to_string())),
@@ -85,8 +84,8 @@ mod test_utils {
ValueType::Number(Some(1200.to_string())),
ValueType::List(Some(vec![
ValueType::String(Some("9".to_string())),
ValueType::String(Some("10".to_string()))
]))
ValueType::String(Some("10".to_string())),
])),
],
);
@@ -96,7 +95,7 @@ mod test_utils {
"last_name".to_string(),
"gender".to_string(),
"sat_score".to_string(),
"grades_participated".to_string()
"grades_participated".to_string(),
],
vec![
ValueType::String(Some("Jane".to_string())),
@@ -105,8 +104,8 @@ mod test_utils {
ValueType::Number(Some(1000.to_string())),
ValueType::List(Some(vec![
ValueType::String(Some("11".to_string())),
ValueType::String(Some("12".to_string()))
]))
ValueType::String(Some("12".to_string())),
])),
],
);
+4 -2
View File
@@ -1,8 +1,10 @@
mod comparison;
mod expandable_info;
mod form_params;
mod form_schema;
mod user;
mod value_type;
mod form_params;
pub use self::{comparison::*, expandable_info::*, form_schema::*, user::*, value_type::*, form_params::*};
pub use self::{
comparison::*, expandable_info::*, form_params::*, form_schema::*, user::*, value_type::*,
};
+2 -1
View File
@@ -74,7 +74,8 @@ pub fn MultiEntry(
#[prop()]
data_map: RwSignal<HashMap<String, ValueType>>,
/// Currently unused.
#[prop(optional, into)] name_member: String,
#[prop(optional, into)]
name_member: String,
/// The type and order of the inputs for each `Entry` component within this `MultiEntry` component.
#[prop(optional)]
schema: Vec<InputType>,
+57 -56
View File
@@ -2,184 +2,185 @@
use crate::pages::utils::server_utils::create_dynamo_client;
#[cfg(feature = "ssr")]
use aws_sdk_dynamodb::{
error::ProvideErrorMetadata,
};
use aws_sdk_dynamodb::error::ProvideErrorMetadata;
use crate::common::{
ComparisonData, ComparisonType, MapListComparison, NumberComparison, NumberListComparison,
TextComparison, TextListComparison, ValueType,
};
use leptos::leptos_dom::log;
use leptos::prelude::ServerFnError;
use leptos::server;
use crate::common::{ComparisonData, ComparisonType, MapListComparison, NumberComparison, NumberListComparison, TextComparison, TextListComparison, ValueType};
/// 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.
fn create_sports_comparisons() -> Vec<ComparisonData> {
vec! [
vec![
ComparisonData::new(
"sports_football",
"sports_participation",
ComparisonType::MapList(MapListComparison::FlattenToTextList(
"sport_name".to_string(),
Box::new(TextListComparison::Contains)
Box::new(TextListComparison::Contains),
)),
ValueType::String(Some("Football".to_string())),
"Sports Participation",
"Football"
"Football",
),
ComparisonData::new(
"sports_soccer",
"sports_participation",
ComparisonType::MapList(MapListComparison::FlattenToTextList(
"sport_name".to_string(),
Box::new(TextListComparison::Contains)
Box::new(TextListComparison::Contains),
)),
ValueType::String(Some("Soccer".to_string())),
"Sports Participation",
"Soccer"
"Soccer",
),
ComparisonData::new(
"sports_cheerleading",
"sports_participation",
ComparisonType::MapList(MapListComparison::FlattenToTextList(
"sport_name".to_string(),
Box::new(TextListComparison::Contains)
Box::new(TextListComparison::Contains),
)),
ValueType::String(Some("Cheerleading".to_string())),
"Sports Participation",
"Cheerleading"
"Cheerleading",
),
ComparisonData::new(
"sports_field_hockey",
"sports_participation",
ComparisonType::MapList(MapListComparison::FlattenToTextList(
"sport_name".to_string(),
Box::new(TextListComparison::Contains)
Box::new(TextListComparison::Contains),
)),
ValueType::String(Some("Field Hockey".to_string())),
"Sports Participation",
"Field Hockey"
"Field Hockey",
),
ComparisonData::new(
"sports_swimming",
"sports_participation",
ComparisonType::MapList(MapListComparison::FlattenToTextList(
"sport_name".to_string(),
Box::new(TextListComparison::Contains)
Box::new(TextListComparison::Contains),
)),
ValueType::String(Some("Swimming".to_string())),
"Sports Participation",
"Swimming"
"Swimming",
),
ComparisonData::new(
"sports_golf",
"sports_participation",
ComparisonType::MapList(MapListComparison::FlattenToTextList(
"sport_name".to_string(),
Box::new(TextListComparison::Contains)
Box::new(TextListComparison::Contains),
)),
ValueType::String(Some("Golf".to_string())),
"Sports Participation",
"Golf"
"Golf",
),
ComparisonData::new(
"sports_basketball",
"sports_participation",
ComparisonType::MapList(MapListComparison::FlattenToTextList(
"sport_name".to_string(),
Box::new(TextListComparison::Contains)
Box::new(TextListComparison::Contains),
)),
ValueType::String(Some("Basketball".to_string())),
"Sports Participation",
"Basketball"
"Basketball",
),
ComparisonData::new(
"sports_track",
"sports_participation",
ComparisonType::MapList(MapListComparison::FlattenToTextList(
"sport_name".to_string(),
Box::new(TextListComparison::Contains)
Box::new(TextListComparison::Contains),
)),
ValueType::String(Some("Track".to_string())),
"Sports Participation",
"Track"
"Track",
),
ComparisonData::new(
"sports_gymnastics",
"sports_participation",
ComparisonType::MapList(MapListComparison::FlattenToTextList(
"sport_name".to_string(),
Box::new(TextListComparison::Contains)
Box::new(TextListComparison::Contains),
)),
ValueType::String(Some("Gymnastics".to_string())),
"Sports Participation",
"Gymnastics"
"Gymnastics",
),
ComparisonData::new(
"sports_ice_hockey",
"sports_participation",
ComparisonType::MapList(MapListComparison::FlattenToTextList(
"sport_name".to_string(),
Box::new(TextListComparison::Contains)
Box::new(TextListComparison::Contains),
)),
ValueType::String(Some("Ice Hockey".to_string())),
"Sports Participation",
"Ice Hockey"
"Ice Hockey",
),
ComparisonData::new(
"sports_ski",
"sports_participation",
ComparisonType::MapList(MapListComparison::FlattenToTextList(
"sport_name".to_string(),
Box::new(TextListComparison::Contains)
Box::new(TextListComparison::Contains),
)),
ValueType::String(Some("Ski".to_string())),
"Sports Participation",
"Ski"
"Ski",
),
ComparisonData::new(
"sports_wrestling",
"sports_participation",
ComparisonType::MapList(MapListComparison::FlattenToTextList(
"sport_name".to_string(),
Box::new(TextListComparison::Contains)
Box::new(TextListComparison::Contains),
)),
ValueType::String(Some("Wrestling".to_string())),
"Sports Participation",
"Wrestling"
"Wrestling",
),
ComparisonData::new(
"sports_lacrosse",
"sports_participation",
ComparisonType::MapList(MapListComparison::FlattenToTextList(
"sport_name".to_string(),
Box::new(TextListComparison::Contains)
Box::new(TextListComparison::Contains),
)),
ValueType::String(Some("Lacrosse".to_string())),
"Sports Participation",
"Lacrosse"
"Lacrosse",
),
ComparisonData::new(
"sports_softball",
"sports_participation",
ComparisonType::MapList(MapListComparison::FlattenToTextList(
"sport_name".to_string(),
Box::new(TextListComparison::Contains)
Box::new(TextListComparison::Contains),
)),
ValueType::String(Some("Softball".to_string())),
"Sports Participation",
"Softball"
"Softball",
),
ComparisonData::new(
"sports_tennis",
"sports_participation",
ComparisonType::MapList(MapListComparison::FlattenToTextList(
"sport_name".to_string(),
Box::new(TextListComparison::Contains)
Box::new(TextListComparison::Contains),
)),
ValueType::String(Some("Tennis".to_string())),
"Sports Participation",
"Tennis"
)
"Tennis",
),
]
}
@@ -195,7 +196,7 @@ pub async fn create_test_comparisons() -> Result<(), ServerFnError> {
ComparisonType::Number(NumberComparison::GreaterThanOrEqual),
ValueType::Number(Some(650.to_string())),
"Academic Information",
"Math SAT Score > 650"
"Math SAT Score > 650",
);
let service_hours_25 = ComparisonData::new(
@@ -203,13 +204,13 @@ pub async fn create_test_comparisons() -> Result<(), ServerFnError> {
"community_involvement",
ComparisonType::MapList(MapListComparison::FlattenToNumberList(
"service_hours".to_string(),
Box::new(NumberListComparison::Sum(
Box::new(NumberComparison::GreaterThanOrEqual)
))
Box::new(NumberListComparison::Sum(Box::new(
NumberComparison::GreaterThanOrEqual,
))),
)),
ValueType::Number(Some(25.to_string())),
"Community Involvement",
"25 service hours"
"25 service hours",
);
let service_hours_20 = ComparisonData::new(
@@ -217,13 +218,13 @@ pub async fn create_test_comparisons() -> Result<(), ServerFnError> {
"community_involvement",
ComparisonType::MapList(MapListComparison::FlattenToNumberList(
"service_hours".to_string(),
Box::new(NumberListComparison::Sum(
Box::new(NumberComparison::GreaterThanOrEqual)
))
Box::new(NumberListComparison::Sum(Box::new(
NumberComparison::GreaterThanOrEqual,
))),
)),
ValueType::Number(Some(20.to_string())),
"Community Involvement",
"20 service hours"
"20 service hours",
);
let service_hours_30 = ComparisonData::new(
@@ -231,13 +232,13 @@ pub async fn create_test_comparisons() -> Result<(), ServerFnError> {
"community_involvement",
ComparisonType::MapList(MapListComparison::FlattenToNumberList(
"service_hours".to_string(),
Box::new(NumberListComparison::Sum(
Box::new(NumberComparison::GreaterThanOrEqual)
))
Box::new(NumberListComparison::Sum(Box::new(
NumberComparison::GreaterThanOrEqual,
))),
)),
ValueType::Number(Some(30.to_string())),
"Community Involvement",
"30 service hours"
"30 service hours",
);
let residency_southbury = ComparisonData::new(
@@ -246,7 +247,7 @@ pub async fn create_test_comparisons() -> Result<(), ServerFnError> {
ComparisonType::Text(TextComparison::Matches),
ValueType::String(Some("Southbury".to_string())),
"Residency",
"Southbury"
"Southbury",
);
let residency_middlebury = ComparisonData::new(
@@ -255,7 +256,7 @@ pub async fn create_test_comparisons() -> Result<(), ServerFnError> {
ComparisonType::Text(TextComparison::Matches),
ValueType::String(Some("Middlebury".to_string())),
"Residency",
"Middlebury"
"Middlebury",
);
let attended_bas = ComparisonData::new(
@@ -264,7 +265,7 @@ pub async fn create_test_comparisons() -> Result<(), ServerFnError> {
ComparisonType::Text(TextComparison::Matches),
ValueType::String(Some("Yes".to_string())),
"Miscellaneous",
"Attended BAS"
"Attended BAS",
);
let midd_south = ComparisonData::new(
@@ -273,7 +274,7 @@ pub async fn create_test_comparisons() -> Result<(), ServerFnError> {
ComparisonType::Text(TextComparison::Matches),
ValueType::String(Some("Yes".to_string())),
"Miscellaneous",
"Member of Midd-South Church"
"Member of Midd-South Church",
);
let family_military = ComparisonData::new(
@@ -282,7 +283,7 @@ pub async fn create_test_comparisons() -> Result<(), ServerFnError> {
ComparisonType::Text(TextComparison::Matches),
ValueType::String(Some("Yes".to_string())),
"Miscellaneous",
"Family with Military Service"
"Family with Military Service",
);
let mut sports_comps = create_sports_comparisons();
@@ -312,7 +313,7 @@ pub async fn create_test_comparisons() -> Result<(), ServerFnError> {
.await
{
let msg = err.message().unwrap_or("An unknown error occurred");
return Err(ServerFnError::new(msg))
return Err(ServerFnError::new(msg));
}
}
@@ -340,7 +341,7 @@ pub async fn get_comparison_info() -> Result<Vec<ComparisonData>, ServerFnError>
} else {
Ok(vec![])
}
},
}
Err(err) => {
let msg = err.message().unwrap_or("An unknown error occurred");
Err(ServerFnError::new(msg))
+7 -7
View File
@@ -1,13 +1,10 @@
#[cfg(feature = "ssr")]
use aws_sdk_dynamodb::{
types::AttributeValue,
error::ProvideErrorMetadata,
};
use aws_sdk_dynamodb::{error::ProvideErrorMetadata, types::AttributeValue};
use crate::common::ExpandableInfo;
use leptos::leptos_dom::log;
use leptos::prelude::ServerFnError;
use leptos::server;
use crate::common::ExpandableInfo;
#[cfg(feature = "ssr")]
use crate::pages::utils::server_utils::create_dynamo_client;
@@ -49,7 +46,10 @@ pub async fn create_scholarship_info(info: ExpandableInfo) -> Result<(), ServerF
let client = create_dynamo_client().await;
log!("Creating or updating scholarship with ID {:?}", info.subject);
log!(
"Creating or updating scholarship with ID {:?}",
info.subject
);
match client
.put_item()
@@ -82,7 +82,7 @@ pub async fn get_all_scholarship_info() -> Result<Vec<ExpandableInfo>, ServerFnE
} else {
Ok(Vec::new())
}
},
}
Err(err) => {
let msg = err.message().unwrap_or("An unknown error occurred");
Err(ServerFnError::new(msg))
+7 -11
View File
@@ -1,15 +1,13 @@
use leptos::either::Either;
use super::UnauthenticatedPage;
use super::api::{get_all_scholarship_info, get_comparison_info};
use super::home_page::get_submission;
use super::{UnauthenticatedPage};
use crate::common::{
ComparisonData, ExpandableInfo, UserClaims, ValueType,
};
use crate::common::{ComparisonData, ExpandableInfo, UserClaims, ValueType};
use crate::components::{ActionButton, Loading, Panel, Row};
use leptos::either::Either;
use leptos::leptos_dom::logging::console_log;
use leptos::logging::log;
use leptos::prelude::*;
use leptos_oidc::{Algorithm, AuthLoaded, AuthSignal, Authenticated};
use super::api::{get_comparison_info, get_all_scholarship_info};
#[component]
pub fn ComparisonTestPage() -> impl IntoView {
@@ -28,9 +26,7 @@ pub fn ComparisonTestPage() -> impl IntoView {
// we don't lose access to it in the future, should we need it again.
let student_resource: Resource<ExpandableInfo> = Resource::new(
move || user_claims.get().map(|claim| claim.claims.subject.clone()),
async
| opt_username
| match opt_username {
async |opt_username| match opt_username {
Some(subject) => get_submission(subject.clone()).await.unwrap_or_else(|e| {
console_log(e.to_string().as_str());
ExpandableInfo::new(subject)
@@ -46,7 +42,7 @@ pub fn ComparisonTestPage() -> impl IntoView {
log!("An error occurred: {:?}", e);
Vec::new()
})
}
},
);
let comparison_resource: Resource<Vec<ComparisonData>> = Resource::new(
@@ -56,7 +52,7 @@ pub fn ComparisonTestPage() -> impl IntoView {
log!("An error occurred: {:?}", e);
Vec::new()
})
}
},
);
view! {
+8 -10
View File
@@ -5,24 +5,24 @@ use aws_sdk_dynamodb::{Client, error::ProvideErrorMetadata, types::AttributeValu
#[cfg(feature = "ssr")]
use serde_dynamo::{from_item, to_item};
#[cfg(feature = "ssr")]
use std::process::{Command, Stdio};
#[cfg(feature = "ssr")]
use std::io::Write;
#[cfg(feature = "ssr")]
use std::process::{Command, Stdio};
use crate::common::{ExpandableInfo, UserClaims};
use crate::pages::UnauthenticatedPage;
use crate::components::{
ActionButton, CheckboxList, Loading, MultiEntry, OutlinedTextField, Panel, RadioList, Row,
Select
Select,
};
use crate::input;
use crate::pages::UnauthenticatedPage;
use base64::Engine;
use leptos::leptos_dom::logging::console_log;
use leptos::prelude::*;
use leptos_oidc::{Algorithm, AuthLoaded, AuthSignal, Authenticated};
use base64::Engine;
use leptos::task::spawn_local;
use leptos::web_sys::HtmlAnchorElement;
use leptos::wasm_bindgen::JsCast;
use leptos::web_sys::HtmlAnchorElement;
use leptos_oidc::{Algorithm, AuthLoaded, AuthSignal, Authenticated};
use traits::{AsReactive, ReactiveCapture};
/// # Get Student Info
@@ -132,9 +132,7 @@ pub async fn create_example_pdf() -> Result<Vec<u8>, ServerFnError> {
let mut process = command.spawn()?;
process.stdin
.take().unwrap()
.write(template.as_bytes())?;
process.stdin.take().unwrap().write(template.as_bytes())?;
let output = process.wait_with_output()?;
let pdf_bytes = output.stdout;
+66 -33
View File
@@ -4,23 +4,25 @@ use aws_sdk_dynamodb::{Client, error::ProvideErrorMetadata, types::AttributeValu
#[cfg(feature = "ssr")]
use serde_dynamo::{from_item, to_item};
use leptos::either::{Either, EitherOf3};
use leptos::prelude::*;
use serde::{Deserialize, Serialize};
use leptos::logging::log;
use leptos_router::params::Params;
use leptos_router::hooks::{use_navigate, use_params};
use leptos::Params;
use traits::{AsReactive, ReactiveCapture};
use crate::common::{ExpandableInfo, ValueType};
use crate::components::{ActionButton, Banner, DashboardButton, OutlinedTextField, Panel, Row, Select};
use std::collections::HashMap;
use crate::components::{
ActionButton, Banner, DashboardButton, OutlinedTextField, Panel, Row, Select,
};
use chrono::{FixedOffset, TimeZone};
use leptos::Params;
use leptos::either::{Either, EitherOf3};
use leptos::html::Dialog;
use leptos::logging::log;
use leptos::prelude::*;
use leptos_router::hooks::{use_navigate, use_params};
use leptos_router::params::Params;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use traits::{AsReactive, ReactiveCapture};
#[derive(Params, PartialEq)]
struct LoanerParams {
form_name: Option<String>
form_name: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
@@ -39,7 +41,8 @@ struct LoanerReturnOutput {
async fn get_loaners_return_list() -> Result<Vec<LoanerReturnOutput>, ServerFnError> {
// We want to query the database for all loaners, but we only want the name and
// date of borrowing. This will be cheaper than querying the entire table.
let dbclient = Client::new(&aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await);
let dbclient =
Client::new(&aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await);
log!("Getting loaners from database.");
@@ -48,34 +51,57 @@ async fn get_loaners_return_list() -> Result<Vec<LoanerReturnOutput>, ServerFnEr
.table_name("loaner-info")
.projection_expression("subject, first_name, last_name, date_taken")
.send()
.await {
.await
{
Ok(output) => {
if let Some(items) = output.items {
Ok(items.into_iter().map(|item| {
Ok(items
.into_iter()
.map(|item| {
let expandable: ExpandableInfo = from_item(item).unwrap();
// This set of function calls is ridiculous, we need to make a better way to do this.
LoanerReturnOutput {
first_name: expandable.data.get("first_name").unwrap().as_string().unwrap_or_default().unwrap_or_default(),
last_name: expandable.data.get("last_name").unwrap().as_string().unwrap_or_default().unwrap_or_default(),
date: expandable.data.get("date_taken").unwrap().as_string().unwrap_or_default().unwrap_or_default(),
subject: expandable.subject
first_name: expandable
.data
.get("first_name")
.unwrap()
.as_string()
.unwrap_or_default()
.unwrap_or_default(),
last_name: expandable
.data
.get("last_name")
.unwrap()
.as_string()
.unwrap_or_default()
.unwrap_or_default(),
date: expandable
.data
.get("date_taken")
.unwrap()
.as_string()
.unwrap_or_default()
.unwrap_or_default(),
subject: expandable.subject,
}
}).collect())
})
.collect())
} else {
Ok(vec![])
}
},
}
Err(err) => {
log!("{:?}", err);
let msg = err.message().unwrap_or("Unknown error");
Err(ServerFnError::new(msg))
},
}
}
}
#[server]
async fn check_in_loaner(student: LoanerReturnOutput) -> Result<(), ServerFnError> {
let dbclient = Client::new(&aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await);
let dbclient =
Client::new(&aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await);
match dbclient
.delete_item()
@@ -96,13 +122,17 @@ async fn check_in_loaner(student: LoanerReturnOutput) -> Result<(), ServerFnErro
#[server]
async fn create_borrow_entry(input: ExpandableInfo) -> Result<(), ServerFnError> {
let mut input = input;
let dbclient = Client::new(&aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await);
let dbclient =
Client::new(&aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await);
let current_time = chrono::Utc::now().format("%H:%M, %m/%d/%Y").to_string();
log!("Creating entry with timestamp {}", current_time);
// Add the timestamp to the data map as a string and then push it to the database.
input.data.insert("date_taken".to_string(), ValueType::String(Some(current_time.clone())));
input.data.insert(
"date_taken".to_string(),
ValueType::String(Some(current_time.clone())),
);
loop {
let item: serde_dynamo::Item = to_item(&input)?;
@@ -122,8 +152,8 @@ async fn create_borrow_entry(input: ExpandableInfo) -> Result<(), ServerFnError>
// Retry database writes until we have a good subject ID.
log!("Failed conditional check, forcing new subject ID.");
input.subject = uuid::Uuid::new_v4().to_string();
continue
},
continue;
}
_ => {
let msg = err.message().unwrap_or("An unknown error occurred.");
return Err(ServerFnError::new(msg));
@@ -165,19 +195,18 @@ pub fn LoanerPage() -> impl IntoView {
// Check URL params. Depending on what string is visible here, we'll display a different form.
let params = use_params::<LoanerParams>();
let form_passed = move || {
params.read()
params
.read()
.as_ref()
.ok()
.and_then(|params| params.form_name.clone())
.unwrap_or_default()
};
let form_view = move || {
match form_passed().as_str() {
let form_view = move || match form_passed().as_str() {
"borrowing" => EitherOf3::A(LoanerBorrowForm),
"returning" => EitherOf3::B(LoanerReturnForm),
_ => EitherOf3::C("Choose something from the left.".into_view())
}
_ => EitherOf3::C("Choose something from the left.".into_view()),
};
// We'll create the page here - the sidebar contains all the buttons, while the form
@@ -305,10 +334,12 @@ pub fn LoanerReturnForm() -> impl IntoView {
let return_list_refresh = RwSignal::new(0);
let return_list_resource: Resource<Vec<LoanerReturnOutput>> = Resource::new(
move || return_list_refresh.get(),
async |_| get_loaners_return_list().await.unwrap_or_else(|e| {
async |_| {
get_loaners_return_list().await.unwrap_or_else(|e| {
log!("There was an error getting the loaner list: {}", e);
vec![]
})
},
);
let dialog_ref = NodeRef::<Dialog>::new();
@@ -340,7 +371,9 @@ pub fn LoanerReturnForm() -> impl IntoView {
let dialog_check_in = move |_| {
chosen_student.with(|opt_student| {
if let Some(student) = opt_student.as_ref() {
check_in_action.dispatch(CheckInLoaner { student: student.clone() });
check_in_action.dispatch(CheckInLoaner {
student: student.clone(),
});
}
})
};
+5 -5
View File
@@ -1,15 +1,15 @@
mod about_page;
mod api;
mod comparison_test;
mod home_page;
mod loaner_page;
mod provider_portal;
mod scholarship_info;
mod test_page;
mod unauthenticated_page;
mod scholarship_info;
mod utils;
mod api;
mod loaner_page;
pub use self::{
about_page::*, home_page::*, provider_portal::*, test_page::*, unauthenticated_page::*,
loaner_page::LoanerPage, comparison_test::*,
about_page::*, comparison_test::*, home_page::*, loaner_page::LoanerPage, provider_portal::*,
test_page::*, unauthenticated_page::*,
};
+41
View File
@@ -1 +1,42 @@
use crate::components::{Banner, DashboardButton};
use leptos::prelude::*;
#[component]
pub fn ProviderPortal() -> impl IntoView {
view! {
<Banner
title="Provider Dashboard"
logo="PHS_Stacked_Acronym.png"
path="/"
/>
<div class="mx-auto px-6">
<div class="container mx-auto px-6 py-8 space-y-8">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<section class="space-y-4">
<DashboardButton
title="Profile"
description="Edit user profile"
icon="/Person_Black.png"
path="/"
/>
<DashboardButton
title="Create Scholarship"
description="Navigate to Scholarship Creation Page"
icon="/Create_Black.png"
path="/"
/>
<DashboardButton
title="Applicants"
description="View Scholarship Applicants"
icon="/Form_Black.png"
path="/"
/>
</section>
<section class="space-y-4">
</section>
</div>
</div>
</div>
}
}
+17 -13
View File
@@ -1,14 +1,17 @@
use super::UnauthenticatedPage;
use super::api::{
CreateScholarshipInfo, CreateTestComparisons, get_comparison_info, get_scholarship_info,
};
use crate::common::ComparisonData;
use crate::common::{ExpandableInfo, ScholarshipFormParams};
use crate::components::{
ActionButton, ChipsList, Loading, OutlinedTextField, Panel, RadioList, Row,
};
use leptos::logging::log;
use leptos::prelude::*;
use leptos_oidc::{AuthLoaded, Authenticated};
use leptos_router::hooks::use_params;
use crate::common::{ExpandableInfo, ScholarshipFormParams};
use crate::components::{ActionButton, ChipsList, Loading, OutlinedTextField, Panel, RadioList, Row};
use super::UnauthenticatedPage;
use traits::{AsReactive, ReactiveCapture};
use crate::common::ComparisonData;
use super::api::{get_comparison_info, get_scholarship_info, CreateScholarshipInfo, CreateTestComparisons};
/// # Scholarship Info Page
/// This page will handle creating or editing scholarship information given a specific
@@ -23,7 +26,8 @@ pub fn ScholarshipInfoPage() -> impl IntoView {
// let scholarship_id = String::from("test-scholarship-id2");
let url_params = use_params::<ScholarshipFormParams>();
let scholarship_id = move || {
url_params.read()
url_params
.read()
.as_ref()
.ok()
.and_then(|params| params.id.clone())
@@ -33,7 +37,8 @@ pub fn ScholarshipInfoPage() -> impl IntoView {
let get_scholarship_action: Resource<ExpandableInfo> = Resource::new(
move || scholarship_id().clone(),
async |id| {
get_scholarship_info(id.clone()).await
get_scholarship_info(id.clone())
.await
// Note that this really should notify us of an error and then redirect or ask
// the user to try again.
.unwrap_or_else(|e| {
@@ -41,18 +46,17 @@ pub fn ScholarshipInfoPage() -> impl IntoView {
log!("Using default ExpandableInfo");
ExpandableInfo::new(id)
})
}
},
);
let get_comparison_info: Resource<Vec<ComparisonData>> = Resource::new(
move || Option::<String>::None, // There's no input to this function.
async |_| {
get_comparison_info().await
.unwrap_or_else(|e| {
get_comparison_info().await.unwrap_or_else(|e| {
log!("Failed to get comparison info: {:?}", e);
Vec::new()
})
}
},
);
let create_scholarship_action = ServerAction::<CreateScholarshipInfo>::new();
@@ -102,7 +106,7 @@ pub fn ScholarshipInfoPage() -> impl IntoView {
fn ScholarshipForm(
response: ExpandableInfo,
comparison_list: Vec<ComparisonData>,
submit_action: ServerAction<CreateScholarshipInfo>
submit_action: ServerAction<CreateScholarshipInfo>,
) -> impl IntoView {
let reactive_info = response.as_reactive();
let elements_disabled = RwSignal::new(false);
+1 -1
View File
@@ -1,5 +1,5 @@
use leptos::logging::log;
use crate::components::ActionButton;
use leptos::logging::log;
use leptos::prelude::*;
use leptos_oidc::AuthSignal;