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:
17 files changed
+348
-262
No files matched your search
+16
-10
@@ -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! {
|
||||
@@ -32,7 +35,7 @@ pub fn App() -> impl IntoView {
|
||||
// injects a stylesheet into the document <head>
|
||||
// id=leptos means cargo-leptos will hot-reload this stylesheet
|
||||
<Stylesheet id="leptos" href="/pkg/scholarships-rs-wasm.css"/>
|
||||
|
||||
|
||||
// sets the document title
|
||||
<Title text="R15 Scholarship App DEV"/>
|
||||
|
||||
@@ -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()
|
||||
@@ -53,8 +57,9 @@ fn use_origin() -> String {
|
||||
.origin()
|
||||
.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())
|
||||
}
|
||||
@@ -63,7 +68,7 @@ fn use_origin() -> String {
|
||||
#[component]
|
||||
pub fn AppWithRoutes() -> impl IntoView {
|
||||
provide_meta_context();
|
||||
|
||||
|
||||
let current_origin = use_origin();
|
||||
log!("Current origin: {}", current_origin);
|
||||
|
||||
@@ -82,7 +87,7 @@ pub fn AppWithRoutes() -> impl IntoView {
|
||||
|
||||
let auth = Auth::signal();
|
||||
provide_context(auth); // allows use of this signal in lower areas of the tree without
|
||||
// explicitly passing it through the html tree
|
||||
// explicitly passing it through the html tree
|
||||
|
||||
let _ = Auth::init(parameters);
|
||||
|
||||
@@ -96,7 +101,7 @@ pub fn AppWithRoutes() -> impl IntoView {
|
||||
|
||||
// sets the document title
|
||||
<Title text="R15 Scholarship App DEV" />
|
||||
|
||||
|
||||
<main>
|
||||
// TODO Create a 404 page
|
||||
<Routes fallback=|| "Page not found.".into_view()>
|
||||
@@ -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>
|
||||
|
||||
@@ -40,10 +40,15 @@ impl ComparisonData {
|
||||
display_text: display_text.into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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,13 +78,13 @@ 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() {
|
||||
let student_data = create_student_data();
|
||||
|
||||
|
||||
// Should succeed every time.
|
||||
let text_comp_success = ComparisonData {
|
||||
id: "first_name_success".to_string(),
|
||||
@@ -99,11 +104,11 @@ mod tests {
|
||||
category: "Basic Checks".to_string(),
|
||||
display_text: "First Name is Jane".to_string(),
|
||||
};
|
||||
|
||||
|
||||
let mut result = text_comp_success.compare(&student_data);
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), true);
|
||||
|
||||
|
||||
result = text_comp_fail.compare(&student_data);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ impl Comparison for NumberListComparison {
|
||||
|
||||
/// Represents all available comparisons for a text list. All comparisons are comparable to a
|
||||
/// single value.
|
||||
///
|
||||
///
|
||||
/// For example, using `TextListComparison::Contains` checks if a specific value is contained
|
||||
/// in the list.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
@@ -148,12 +148,12 @@ impl Comparison for TextListComparison {
|
||||
/// For right now, nested lists should be flattened into a list of a single type before they
|
||||
/// are compared. The plan is to create a more robust framework that eventually be able to
|
||||
/// query specific lists and perform comparisons on them.
|
||||
///
|
||||
///
|
||||
/// For example:
|
||||
/// ```
|
||||
/// NestedListComparison::FlattenToTextList(Box::new(TextListComparison::Contains));
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// This will flatten a nested list into a single list of `ValueType::String` enums and then
|
||||
/// will check if the list contains the indicated target value.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
@@ -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() {
|
||||
@@ -415,61 +417,59 @@ mod tests {
|
||||
assert_eq!(incorrect_value.unwrap(), false);
|
||||
assert_eq!(mixed_incorrect_type.unwrap(), false);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn text_list_contains() {
|
||||
let (none_list, empty_list) = create_empty_lists();
|
||||
let num_list = create_number_list();
|
||||
let text_list = create_text_list();
|
||||
|
||||
|
||||
// Define comparisons here
|
||||
let comparison = ComparisonType::TextList(TextListComparison::Contains);
|
||||
let target_value = ValueType::String(Some("one".to_string()));
|
||||
|
||||
|
||||
let none_result = comparison.evaluate(&none_list, &target_value);
|
||||
let empty_result = comparison.evaluate(&empty_list, &target_value);
|
||||
let num_result = comparison.evaluate(&num_list, &target_value);
|
||||
let text_list = comparison.evaluate(&text_list, &target_value);
|
||||
|
||||
|
||||
assert_eq!(none_result.unwrap(), false);
|
||||
assert_eq!(empty_result.unwrap(), false);
|
||||
assert_eq!(num_result.unwrap(), false);
|
||||
assert_eq!(text_list.unwrap(), true);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
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);
|
||||
|
||||
|
||||
// This comparison does not use a target value, as it should just check if the list
|
||||
// either doesn't exist or is empty.
|
||||
let mut result = empty_comp.evaluate(&empty_list, &ValueType::List(None));
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), true);
|
||||
|
||||
|
||||
result = empty_comp.evaluate(&none_list, &ValueType::List(None));
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), true);
|
||||
|
||||
|
||||
result = empty_comp.evaluate(&long_list, &ValueType::List(None));
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), false);
|
||||
|
||||
|
||||
result = not_empty_comp.evaluate(&empty_list, &ValueType::List(None));
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), false);
|
||||
|
||||
|
||||
result = not_empty_comp.evaluate(&none_list, &ValueType::List(None));
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), false);
|
||||
|
||||
|
||||
result = not_empty_comp.evaluate(&long_list, &ValueType::List(None));
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), true);
|
||||
|
||||
@@ -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())),
|
||||
])),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -115,14 +114,14 @@ mod test_utils {
|
||||
ValueType::Map(Some(map_two)),
|
||||
]))
|
||||
}
|
||||
|
||||
|
||||
pub fn create_empty_lists() -> (ValueType, ValueType) {
|
||||
let none_list = ValueType::List(None);
|
||||
let empty_list = ValueType::List(Some(vec![]));
|
||||
|
||||
|
||||
(none_list, empty_list)
|
||||
}
|
||||
|
||||
|
||||
pub fn create_number_list() -> ValueType {
|
||||
ValueType::List(Some(vec![
|
||||
ValueType::Number(Some(1.to_string())),
|
||||
@@ -132,7 +131,7 @@ mod test_utils {
|
||||
ValueType::Number(Some(5.to_string())),
|
||||
]))
|
||||
}
|
||||
|
||||
|
||||
pub fn create_text_list() -> ValueType {
|
||||
ValueType::List(Some(vec![
|
||||
ValueType::String(Some("one".to_string())),
|
||||
|
||||
+4
-2
@@ -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::*,
|
||||
};
|
||||
@@ -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>,
|
||||
|
||||
@@ -2,189 +2,190 @@
|
||||
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",
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
#[server(CreateTestComparisons, endpoint = "/comparisons/create-test")]
|
||||
pub async fn create_test_comparisons() -> Result<(), ServerFnError> {
|
||||
pub async fn create_test_comparisons() -> Result<(), ServerFnError> {
|
||||
let client = create_dynamo_client().await;
|
||||
|
||||
log!("Creating test comparisons");
|
||||
@@ -195,96 +196,96 @@ 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(
|
||||
"map_sum_comp",
|
||||
"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(
|
||||
"service_hours_20",
|
||||
"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(
|
||||
"service_hours_30",
|
||||
"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(
|
||||
"residency_southbury",
|
||||
"town",
|
||||
ComparisonType::Text(TextComparison::Matches),
|
||||
ValueType::String(Some("Southbury".to_string())),
|
||||
"Residency",
|
||||
"Southbury"
|
||||
"Southbury",
|
||||
);
|
||||
|
||||
|
||||
let residency_middlebury = ComparisonData::new(
|
||||
"residency_middlebury",
|
||||
"town",
|
||||
ComparisonType::Text(TextComparison::Matches),
|
||||
ValueType::String(Some("Middlebury".to_string())),
|
||||
"Residency",
|
||||
"Middlebury"
|
||||
"Middlebury",
|
||||
);
|
||||
|
||||
|
||||
let attended_bas = ComparisonData::new(
|
||||
"attended_bas",
|
||||
"attended_bas",
|
||||
ComparisonType::Text(TextComparison::Matches),
|
||||
ValueType::String(Some("Yes".to_string())),
|
||||
"Miscellaneous",
|
||||
"Attended BAS"
|
||||
"Attended BAS",
|
||||
);
|
||||
|
||||
|
||||
let midd_south = ComparisonData::new(
|
||||
"midd_south",
|
||||
"middsouth_church",
|
||||
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(
|
||||
"family_military",
|
||||
"family_military_service",
|
||||
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();
|
||||
let mut major_comps = Vec::<ComparisonData>::new();
|
||||
|
||||
@@ -299,7 +300,7 @@ pub async fn create_test_comparisons() -> Result<(), ServerFnError> {
|
||||
midd_south,
|
||||
family_military,
|
||||
];
|
||||
|
||||
|
||||
comp_list.append(&mut sports_comps);
|
||||
comp_list.append(&mut major_comps);
|
||||
|
||||
@@ -309,13 +310,13 @@ pub async fn create_test_comparisons() -> Result<(), ServerFnError> {
|
||||
.table_name("leptos-comparison-test")
|
||||
.set_item(Some(serde_dynamo::to_item(&comparison)?))
|
||||
.send()
|
||||
.await
|
||||
.await
|
||||
{
|
||||
let msg = err.message().unwrap_or("An unknown error occurred");
|
||||
return Err(ServerFnError::new(msg))
|
||||
return Err(ServerFnError::new(msg));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
mod comparisons;
|
||||
mod scholarships;
|
||||
|
||||
pub use self::{comparisons::*, scholarships::*};
|
||||
pub use self::{comparisons::*, scholarships::*};
|
||||
@@ -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))
|
||||
|
||||
@@ -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,17 +26,15 @@ 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 {
|
||||
Some(subject) => get_submission(subject.clone()).await.unwrap_or_else(|e| {
|
||||
console_log(e.to_string().as_str());
|
||||
ExpandableInfo::new(subject)
|
||||
}),
|
||||
None => ExpandableInfo::new("".to_owned()),
|
||||
},
|
||||
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)
|
||||
}),
|
||||
None => ExpandableInfo::new("".to_owned()),
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
let scholarship_resource: Resource<Vec<ExpandableInfo>> = Resource::new(
|
||||
move || Option::<String>::None,
|
||||
async |_| {
|
||||
@@ -46,9 +42,9 @@ pub fn ComparisonTestPage() -> impl IntoView {
|
||||
log!("An error occurred: {:?}", e);
|
||||
Vec::new()
|
||||
})
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
let comparison_resource: Resource<Vec<ComparisonData>> = Resource::new(
|
||||
move || Option::<String>::None,
|
||||
async |_| {
|
||||
@@ -56,7 +52,7 @@ pub fn ComparisonTestPage() -> impl IntoView {
|
||||
log!("An error occurred: {:?}", e);
|
||||
Vec::new()
|
||||
})
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
view! {
|
||||
@@ -89,9 +85,9 @@ pub fn ComparisonTestPage() -> impl IntoView {
|
||||
return Ok(Some(scholarship)) // students will always qualify when there are no requirements
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let mut final_result = Ok(true);
|
||||
|
||||
|
||||
// The list should exist, and it should contain a series of strings.
|
||||
for val in list {
|
||||
let comp_opt = match val {
|
||||
@@ -101,7 +97,7 @@ pub fn ComparisonTestPage() -> impl IntoView {
|
||||
}
|
||||
_ => None
|
||||
};
|
||||
|
||||
|
||||
let comparison_outcome = match comp_opt {
|
||||
Some(comp) => {
|
||||
// Perform the comparison
|
||||
@@ -109,7 +105,7 @@ pub fn ComparisonTestPage() -> impl IntoView {
|
||||
}
|
||||
_ => Err("".to_string())
|
||||
};
|
||||
|
||||
|
||||
match comparison_outcome {
|
||||
Ok(true) => {},
|
||||
Ok(false) => final_result = Ok(false),
|
||||
@@ -119,14 +115,14 @@ pub fn ComparisonTestPage() -> impl IntoView {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
match final_result {
|
||||
Ok(true) => Ok(Some(scholarship)),
|
||||
Ok(false) => Ok(None),
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
}).collect::<Vec<Result<Option<&ExpandableInfo>, String>>>();
|
||||
|
||||
|
||||
// Build a display string
|
||||
let mut strings = Vec::new();
|
||||
strings.push("Scholarship results: \n".to_string());
|
||||
@@ -143,9 +139,9 @@ pub fn ComparisonTestPage() -> impl IntoView {
|
||||
Err(msg) => strings.push(msg)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let display_text = strings.join("\n").to_string();
|
||||
|
||||
|
||||
result_msg.set(display_text);
|
||||
}>"Test Comparison"</ActionButton>
|
||||
</Row>
|
||||
|
||||
+10
-12
@@ -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
|
||||
@@ -125,16 +125,14 @@ pub async fn create_example_pdf() -> Result<Vec<u8>, ServerFnError> {
|
||||
let mut command = Command::new("typst");
|
||||
command
|
||||
.arg("compile")
|
||||
.arg("-") // Use stdin as input
|
||||
.arg("-") // Write output to stdout
|
||||
.arg("-") // Use stdin as input
|
||||
.arg("-") // Write output to stdout
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped());
|
||||
|
||||
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;
|
||||
|
||||
+81
-48
@@ -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| {
|
||||
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
|
||||
}
|
||||
}).collect())
|
||||
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,
|
||||
}
|
||||
})
|
||||
.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() {
|
||||
"borrowing" => EitherOf3::A(LoanerBorrowForm),
|
||||
"returning" => EitherOf3::B(LoanerReturnForm),
|
||||
_ => EitherOf3::C("Choose something from the left.".into_view())
|
||||
}
|
||||
|
||||
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()),
|
||||
};
|
||||
|
||||
// We'll create the page here - the sidebar contains all the buttons, while the form
|
||||
@@ -221,21 +250,21 @@ pub fn LoanerBorrowForm() -> impl IntoView {
|
||||
let temp = ExpandableInfo::new(uuid::Uuid::new_v4().to_string());
|
||||
let temp_reactive = temp.as_reactive();
|
||||
let elements_disabled = RwSignal::new(false);
|
||||
|
||||
|
||||
view! {
|
||||
<div class="flex flex-col justify-center gap-2 mb-1 py-3 px-2">
|
||||
<h2 class="text-2xl font-bold">"Borrower Information"</h2>
|
||||
<p class="text-lg">"Please fill out the form below."</p>
|
||||
</div>
|
||||
<Row>
|
||||
<OutlinedTextField
|
||||
<OutlinedTextField
|
||||
label="First Name:"
|
||||
placeholder="John"
|
||||
disabled=elements_disabled
|
||||
data_member="first_name"
|
||||
data_map=temp_reactive.data
|
||||
/>
|
||||
<OutlinedTextField
|
||||
<OutlinedTextField
|
||||
label="Last Name:"
|
||||
placeholder="Smith"
|
||||
disabled=elements_disabled
|
||||
@@ -244,7 +273,7 @@ pub fn LoanerBorrowForm() -> impl IntoView {
|
||||
/>
|
||||
</Row>
|
||||
<Row>
|
||||
<OutlinedTextField
|
||||
<OutlinedTextField
|
||||
label="Region 15 Email:"
|
||||
placeholder="example@region15.org"
|
||||
disabled=elements_disabled
|
||||
@@ -268,7 +297,7 @@ pub fn LoanerBorrowForm() -> impl IntoView {
|
||||
/>
|
||||
</Row>
|
||||
<Row>
|
||||
<Select
|
||||
<Select
|
||||
label="Loan Taken:"
|
||||
value_list=vec![
|
||||
"Chromebook".to_string(),
|
||||
@@ -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| {
|
||||
log!("There was an error getting the loaner list: {}", e);
|
||||
vec![]
|
||||
})
|
||||
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
@@ -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::*,
|
||||
};
|
||||
@@ -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>
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,24 @@
|
||||
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
|
||||
/// This page will handle creating or editing scholarship information given a specific
|
||||
/// scholarship ID and scholarship provider subject number. The form itself will only
|
||||
/// track a single scholarship (for now) and so selection of what scholarship will be edited
|
||||
/// must be handled *before* navigation to this page.
|
||||
///
|
||||
///
|
||||
/// In its current testing form, we'll be using a single fixed ID. This absolutely *MUST* be
|
||||
/// changed before a full release.
|
||||
#[component]
|
||||
@@ -23,17 +26,19 @@ 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())
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
|
||||
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,23 +46,22 @@ 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.
|
||||
move || Option::<String>::None, // There's no input to this function.
|
||||
async |_| {
|
||||
get_comparison_info().await
|
||||
.unwrap_or_else(|e| {
|
||||
log!("Failed to get comparison info: {:?}", e);
|
||||
Vec::new()
|
||||
})
|
||||
}
|
||||
get_comparison_info().await.unwrap_or_else(|e| {
|
||||
log!("Failed to get comparison info: {:?}", e);
|
||||
Vec::new()
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
let create_scholarship_action = ServerAction::<CreateScholarshipInfo>::new();
|
||||
let create_test_comps = ServerAction::<CreateTestComparisons>::new();
|
||||
|
||||
|
||||
// Remember that we only want to show the page after we've got authentication information
|
||||
// and if there's data for us to use.
|
||||
view! {
|
||||
@@ -69,11 +73,11 @@ pub fn ScholarshipInfoPage() -> impl IntoView {
|
||||
{move || Suspend::new(async move {
|
||||
let response = get_scholarship_action.get();
|
||||
let comparisons = get_comparison_info.get();
|
||||
|
||||
|
||||
response
|
||||
.zip(comparisons)
|
||||
.map(|(response, comparison_list)| view! {
|
||||
<ScholarshipForm
|
||||
<ScholarshipForm
|
||||
response=response
|
||||
comparison_list=comparison_list
|
||||
submit_action=create_scholarship_action
|
||||
@@ -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);
|
||||
@@ -128,7 +132,7 @@ fn ScholarshipForm(
|
||||
// We'll collect the scholarship's name, num_awards, amount_per_award, total_awards,
|
||||
// fafsa_required, award_to, transcript_required, recipient_selection, essay_requirement (and prompt)
|
||||
// award_night_remarks
|
||||
|
||||
|
||||
view! {
|
||||
<Panel>
|
||||
<Row>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use leptos::logging::log;
|
||||
use crate::components::ActionButton;
|
||||
use leptos::logging::log;
|
||||
use leptos::prelude::*;
|
||||
use leptos_oidc::AuthSignal;
|
||||
|
||||
@@ -12,7 +12,7 @@ pub fn UnauthenticatedPage() -> impl IntoView {
|
||||
.map(|unauthenticated| unauthenticated.login_url())
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
Effect::new(move || log!("Generated login url: {:?}", login_url()));
|
||||
|
||||
let navigate = leptos_router::hooks::use_navigate();
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/// # Server Utilities
|
||||
///
|
||||
///
|
||||
/// These utility functions can only be used from the server. They are created to use server-side
|
||||
/// dependencies like the AWS SDK. Use of these functions will crash the front end, as these
|
||||
/// dependencies cannot be compiled for WASM (nor should they be).
|
||||
@@ -11,7 +11,7 @@ pub mod server_utils {
|
||||
pub async fn create_aws_config() -> SdkConfig {
|
||||
aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await
|
||||
}
|
||||
|
||||
|
||||
/// Creates a DynamoDB client.
|
||||
pub async fn create_dynamo_client() -> aws_sdk_dynamodb::Client {
|
||||
aws_sdk_dynamodb::Client::new(&create_aws_config().await)
|
||||
|
||||
Reference in new issue
Block a user