// id=leptos means cargo-leptos will hot-reload this stylesheet
-
-
+
+
// sets the document title
-
+
// content for this welcome page
@@ -43,82 +45,64 @@ pub fn App() -> impl IntoView {
}
}
-fn use_origin() -> String {
- #[cfg(target_arch = "wasm32")] {
- use leptos::web_sys;
- // Just get the current URL origin.
- web_sys::window()
- .unwrap()
- .location()
- .origin()
- .unwrap_or("http://localhost:3000/".to_string())
- }
-
- #[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())
- }
-}
-
#[component]
pub fn AppWithRoutes() -> impl IntoView {
provide_meta_context();
-
- let current_origin = use_origin();
-
- // Authentication setup
- let parameters = AuthParameters {
- issuer: "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Lfjuy5zaM".into(),
- client_id: "10jr2h3vtpu9n7gj46pvg5qo2q".into(),
- redirect_uri: format!("{}/", use_origin()),
- post_logout_redirect_uri: current_origin,
- scope: Some("openid%20profile%20email".into()),
- audience: None,
- challenge: Challenge::None,
- };
-
- 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
-
- let _ = Auth::init(parameters);
view! {
// injects a stylesheet into the document
// id=leptos means cargo-leptos will hot-reload this stylesheet
- //
"Authentication is loading"
- //
-
// sets the document title
-
+
// TODO Create a 404 page
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
}
}
+#[component(transparent)]
+fn ProviderRoutes() -> impl MatchNestedRoutes + Clone {
+ view! {
+
+
+
+
+
+
+
+
+
+ }
+ .into_inner()
+ .into_any_nested_route()
+}
+
+#[component(transparent)]
+fn LoanerRoutes() -> impl MatchNestedRoutes + Clone {
+ view! {
+
+
+
+
+
+ }
+ .into_inner()
+ .into_any_nested_route()
+}
+
#[component]
pub fn AuthErrorView() -> impl IntoView {
let auth = use_context::().expect("AuthError: RwSignal was not found");
diff --git a/src/common/date_info.rs b/src/common/date_info.rs
new file mode 100644
index 0000000..269c300
--- /dev/null
+++ b/src/common/date_info.rs
@@ -0,0 +1,63 @@
+use chrono::{DateTime, FixedOffset, Local};
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct DateInfo {
+ pub id: String,
+ pub title: String,
+ pub date: DateRange,
+ pub description: String,
+}
+
+impl DateInfo {
+ pub fn get_start_date(&self) -> DateTime {
+ match self.date {
+ DateRange::Single(date) | DateRange::Range(date, _) => date,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum DateRange {
+ Single(DateTime),
+ Range(DateTime, DateTime),
+}
+
+impl DateRange {
+ pub fn get_status(&self) -> DateStatus {
+ match self {
+ DateRange::Single(_) => DateStatus::Blank,
+ DateRange::Range(start_date, end_date) => {
+ let local = Local::now();
+ // Check the amount of time from now to the start date. Values that are negative
+ // indicate that the start date has already passed.
+ if dbg!(start_date.signed_duration_since(local).num_days()) > 0 {
+ DateStatus::Upcoming
+ } else {
+ // Check the amount of time from now to the end date. The value will be positive if
+ // the end date is in the future. We will be specific to the minute.
+ let duration_until = dbg!(end_date).signed_duration_since(local);
+ let num_days = dbg!(duration_until.num_days());
+ let num_mins = dbg!(duration_until.num_minutes());
+
+ if num_mins <= 0 {
+ DateStatus::Closed
+ } else if num_days <= 5 {
+ DateStatus::Deadline
+ } else {
+ DateStatus::Open
+ }
+ }
+ }
+ }
+ }
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd, Eq, Ord)]
+pub enum DateStatus {
+ Closed,
+ Deadline,
+ Open,
+ Upcoming,
+ Blank,
+}
diff --git a/src/common/expandable_info.rs b/src/common/expandable_info.rs
index 0ef8ff0..a209606 100644
--- a/src/common/expandable_info.rs
+++ b/src/common/expandable_info.rs
@@ -15,7 +15,7 @@ use std::collections::HashMap;
/// now, the table will simply create a single massive `Map` field that contains all the stored
/// information in the `HashMap`. We'd prefer to be able to get single values if possible,
/// especially for interoperability with other applications, should they be created.
-#[derive(Debug, Clone, Serialize, Deserialize, Reactive, )]
+#[derive(Debug, Clone, Serialize, Deserialize, Reactive)]
pub struct ExpandableInfo {
/// The main key member. Required and used to get/put the information in a database.
pub subject: String,
diff --git a/src/common/mod.rs b/src/common/mod.rs
index 5f68c9d..487f959 100644
--- a/src/common/mod.rs
+++ b/src/common/mod.rs
@@ -1,10 +1,14 @@
mod comparison;
+mod date_info;
mod expandable_info;
mod form_params;
mod form_schema;
+mod submit_status;
mod user;
mod value_type;
-mod submit_status;
pub mod animations;
-pub use self::{comparison::*, expandable_info::*, form_schema::*, user::*, value_type::*, form_params::*, submit_status::*};
+pub use self::{
+ comparison::*, date_info::*, expandable_info::*, form_params::*, form_schema::*,
+ submit_status::*, user::*, value_type::*,
+};
diff --git a/src/components/banner.rs b/src/components/banner.rs
index 79b95ca..ac7dce2 100644
--- a/src/components/banner.rs
+++ b/src/components/banner.rs
@@ -32,17 +32,21 @@ pub fn Banner(
view! {
+ }
+}
+
+///# Dates List Component
+/// Displays a list of important dates in a column, with badges that draw attention to dates that
+/// are more important.
+///
+/// Example usage:
+/// ```
+/// let dates = vec![
+/// DateInfo {
+/// title: "Example Title".to_string(),
+/// date: DateRange::Single("January 1st, 1990".to_string()),
+/// description: "A description of the date.".to_string(),
+/// status: DateStatus::Upcoming
+/// }
+/// ]
+///
+/// view! {
+///
+/// }
+/// ```
+#[component]
+pub fn DateList(
+ #[prop(into)] dates: Signal>,
+) -> impl IntoView {
+ view! {
+ {move || {
+ dates
+ .get()
+ .iter()
+ .map(|info| {
+ view! { }
+ })
+ .collect_view()
+ }}
+ }.into_any()
+}
diff --git a/src/components/lists/multi/checkbox.rs b/src/components/lists/multi/checkbox.rs
index 7515b78..252402d 100644
--- a/src/components/lists/multi/checkbox.rs
+++ b/src/components/lists/multi/checkbox.rs
@@ -134,25 +134,26 @@ pub fn CheckboxList(
let cloned = item.clone();
move || cloned.clone()
});
-
let checked = Signal::derive(move || {
controller.selected_list.get().contains(&item.get())
});
-
let on_change = move || {
- // Update the data list by checking if the value is present in it. If so,
- // remove it. If not, add it.
- controller.selected_list.update(|list| {
- let item = item.get();
- if list.contains(&item) {
- list.retain(|val| *val != item);
- } else {
- list.push(item);
- }
- });
+ controller
+ .selected_list
+ .update(|list| {
+ let item = item.get();
+ if list.contains(&item) {
+ list.retain(|val| *val != item);
+ } else {
+ list.push(item);
+ }
+ });
controller.dirty.set(true);
};
+ // Update the data list by checking if the value is present in it. If so,
+ // remove it. If not, add it.
+
view! {
msg,
- _ => "There is no error - should not see this message.".to_string()
+ _ => "There is no error - should not see this message.".to_string(),
}
}}
diff --git a/src/components/lists/multi/chips.rs b/src/components/lists/multi/chips.rs
index d00cd8c..fdd4a22 100644
--- a/src/components/lists/multi/chips.rs
+++ b/src/components/lists/multi/chips.rs
@@ -45,16 +45,17 @@ pub fn Chip(
id=id.clone()
prop:checked=checked
on:change=move |_| on_change.run(())
- disabled=disabled />
+ disabled=disabled
+ />
- {move || displayed_text.get().into_iter().zip(values.get())
- .into_iter()
- .map(|(text, value)| {
- let value = RwSignal::new(value);
+ {move || {
+ displayed_text
+ .get()
+ .into_iter()
+ .zip(values.get())
+ .into_iter()
+ .map(|(text, value)| {
+ let value = RwSignal::new(value);
+ let checked = Signal::derive(move || {
+ controller.selected_list.get().contains(&value.get())
+ });
+ let on_change = move || {
+ let value = value.get();
+ let list = controller.selected_list.get();
+ if list.contains(&value) {
+ allows_multiple
+ .get()
+ .then(|| {
+ controller
+ .selected_list
+ .update(|list| list.retain(|v| v != &value))
+ })
+ .or_else(|| Some(controller.selected_list.set(Vec::new())));
+ } else {
+ allows_multiple
+ .get()
+ .then(|| {
+ controller
+ .selected_list
+ .update(|list| list.push(value.clone()))
+ })
+ .or_else(|| Some(
+ controller.selected_list.set(vec![value.clone()]),
+ ));
+ }
+ controller.dirty.set(true);
+ };
- let checked = Signal::derive(move || {
- controller.selected_list.get().contains(&value.get())
- });
-
- let on_change = move || {
// Update the data list by checking if the value is present in it. If so,
// remove it. If not, add it.
- let value = value.get();
- let list = controller.selected_list.get();
- if list.contains(&value) {
- allows_multiple.get()
- .then(|| controller.selected_list.update(|list| list.retain(|v| v != &value)))
- .or_else(|| Some(controller.selected_list.set(Vec::new())));
- } else {
- allows_multiple.get()
- .then(|| controller.selected_list.update(|list| list.push(value.clone())))
- .or_else(|| Some(controller.selected_list.set(vec![value.clone()])));
+ view! {
+
}
-
- controller.dirty.set(true);
- };
-
- view! {
-
- }
- })
- .collect_view()
- }
+ })
+ .collect_view()
+ }}
@@ -146,7 +160,7 @@ pub fn ChipsList(
{move || {
match controller.error.get() {
ValidationState::Invalid(msg) => msg,
- _ => "There is no error - should not see this message.".to_string()
+ _ => "There is no error - should not see this message.".to_string(),
}
}}
diff --git a/src/components/lists/single/radio.rs b/src/components/lists/single/radio.rs
index 1cdbc25..a554dd7 100644
--- a/src/components/lists/single/radio.rs
+++ b/src/components/lists/single/radio.rs
@@ -108,22 +108,21 @@ fn RadioOther(
// This div displays the box for the checkbox, and applies some styling to the child
// span element.
+ transition-bg duration-200 peer-checked:bg-red-700 peer-checked:border-red-700
+ [&>span]:opacity-0 peer-checked:[&>span]:opacity-100
+ peer-disabled:bg-gray-600/33 peer-disabled:border-gray-600
+ peer-disabled:peer-checked:bg-gray-600">
// This span is the check icon. It is centered within the div and has a border on the bottom
// and right sides, which is then rotated to appear like a checkmark.
-
+
>()}
-
-
-
+
+
+
{move || {
match error.get() {
ValidationState::Invalid(msg) => msg,
- _ => "There is no error - should not see this message.".to_string()
+ _ => "There is no error - should not see this message.".to_string(),
}
}}
diff --git a/src/components/lists/single/select.rs b/src/components/lists/single/select.rs
index 3fd6ab6..b20cddc 100644
--- a/src/components/lists/single/select.rs
+++ b/src/components/lists/single/select.rs
@@ -93,9 +93,9 @@ pub fn Select(
{label}
@@ -122,7 +125,7 @@ pub fn Select(
{move || {
match error.get() {
ValidationState::Invalid(msg) => msg,
- _ => "There is no error - should not see this message.".to_string()
+ _ => "There is no error - should not see this message.".to_string(),
}
}}
diff --git a/src/components/login/mod.rs b/src/components/login/mod.rs
new file mode 100644
index 0000000..fae50eb
--- /dev/null
+++ b/src/components/login/mod.rs
@@ -0,0 +1,5 @@
+mod provider;
+pub use provider::*;
+mod student;
+pub use student::*;
+pub(crate) mod setup;
\ No newline at end of file
diff --git a/src/components/login/provider.rs b/src/components/login/provider.rs
new file mode 100644
index 0000000..ce1fb6d
--- /dev/null
+++ b/src/components/login/provider.rs
@@ -0,0 +1,30 @@
+use leptos::prelude::*;
+use leptos_router::components::Outlet;
+use super::setup::provide_auth_context;
+
+///# Provider Login Context Component
+///
+/// This component handles providing context to the provider login information.
+/// It creates the corresponding [`AuthSignal`], specific to the student login page and redirects.
+///
+/// Example usage:
+/// ```
+/// view! {
+///
+/// // The rest of the student application. Don't forget AuthLoaded and
+/// // Authenticated components!
+///
+/// }
+#[component]
+pub fn ProviderLoginContext() -> impl IntoView {
+ provide_auth_context(
+ crate::utils::use_origin(),
+ "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Lfjuy5zaM",
+ "56c2bqvl021rv8d5mq36blt7jv",
+ "/providers/callback"
+ );
+
+ view! {
+
+ }
+}
diff --git a/src/components/login/setup.rs b/src/components/login/setup.rs
new file mode 100644
index 0000000..a5ebae6
--- /dev/null
+++ b/src/components/login/setup.rs
@@ -0,0 +1,24 @@
+use leptos::prelude::*;
+use leptos_oidc::{Auth, AuthParameters, Challenge};
+
+pub fn provide_auth_context(
+ origin: String,
+ issuer: impl Into,
+ client_id: impl Into,
+ redirect_path: impl Into,
+) {
+ let params = AuthParameters {
+ issuer: issuer.into(),
+ client_id: client_id.into(),
+ redirect_uri: format!("{}{}", origin, redirect_path.into()),
+ post_logout_redirect_uri: origin,
+ scope: Some("openid%20profile%20email".into()),
+ audience: None,
+ challenge: Challenge::None,
+ };
+
+ let auth = Auth::signal();
+ provide_context(auth);
+
+ let _ = Auth::init(params);
+}
diff --git a/src/components/login/student.rs b/src/components/login/student.rs
new file mode 100644
index 0000000..f3b89df
--- /dev/null
+++ b/src/components/login/student.rs
@@ -0,0 +1,30 @@
+use leptos::prelude::*;
+use leptos_router::components::Outlet;
+use super::setup::provide_auth_context;
+
+///# Student Login Context Component
+///
+/// This component handles providing context to the student login information.
+/// It creates the corresponding [`AuthSignal`], specific to the student login page and redirects.
+///
+/// Example usage:
+/// ```
+/// view! {
+///
+/// // The rest of the student application. Don't forget AuthLoaded and
+/// // Authenticated components!
+///
+/// }
+#[component]
+pub fn StudentLoginContext() -> impl IntoView {
+ provide_auth_context(
+ crate::utils::use_origin(),
+ "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Lfjuy5zaM",
+ "10jr2h3vtpu9n7gj46pvg5qo2q",
+ "/students/callback"
+ );
+
+ view! {
+
+ }
+}
diff --git a/src/components/mod.rs b/src/components/mod.rs
index a5f210f..27d6b69 100644
--- a/src/components/mod.rs
+++ b/src/components/mod.rs
@@ -1,6 +1,7 @@
mod action_button;
mod banner;
mod dashboard_button;
+mod date;
mod loading;
mod multi_entry;
mod panel;
@@ -11,9 +12,10 @@ mod validated_form;
mod lists;
mod toasts;
mod header;
+pub mod login;
pub use self::{
action_button::*, banner::*, dashboard_button::*, lists::*,
loading::*, multi_entry::*, panel::*, row::*, text_field::*,
- validated_form::*, toasts::*, header::*
+ validated_form::*, toasts::*, header::*, date::*
};
diff --git a/src/components/text_field.rs b/src/components/text_field.rs
index dee1854..40e9052 100644
--- a/src/components/text_field.rs
+++ b/src/components/text_field.rs
@@ -144,12 +144,12 @@ pub fn OutlinedTextField(
msg,
- _ => "There is no error - should not see this message.".to_string()
+ _ => "There is no error - should not see this message.".to_string(),
}
}}
diff --git a/src/lib.rs b/src/lib.rs
index 9958e9f..b512299 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,6 +2,7 @@ pub mod app;
mod common;
mod components;
mod pages;
+pub mod utils;
#[cfg(feature = "hydrate")]
#[wasm_bindgen::prelude::wasm_bindgen]
diff --git a/src/main.rs b/src/main.rs
index 1d9b6f6..2900ec5 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -6,7 +6,12 @@ async fn main() {
use leptos::prelude::*;
use leptos_axum::{LeptosRoutes, generate_route_list};
use scholarships_rs::app::*;
+ use scholarships_rs::utils::server::create_dynamo_client;
+ use std::sync::Arc;
+ let dynamo_client = create_dynamo_client().await;
+ let shared_client = Arc::new(dynamo_client);
+
let conf = get_configuration(None).unwrap();
let addr = conf.leptos_options.site_addr;
let leptos_options = conf.leptos_options;
@@ -14,10 +19,20 @@ async fn main() {
let routes = generate_route_list(App);
let app = Router::new()
- .leptos_routes(&leptos_options, routes, {
- let leptos_options = leptos_options.clone();
- move || shell(leptos_options.clone())
- })
+ .leptos_routes_with_context(
+ &leptos_options,
+ routes,
+ {
+ let shared_client = Arc::clone(&shared_client);
+ move || {
+ provide_context(Arc::clone(&shared_client));
+ }
+ },
+ {
+ let leptos_options = leptos_options.clone();
+ move || shell(leptos_options.clone())
+ }
+ )
.fallback(leptos_axum::file_and_error_handler(shell))
.with_state(leptos_options);
diff --git a/src/pages/api/comparisons.rs b/src/pages/api/comparisons.rs
index daf1339..9177955 100644
--- a/src/pages/api/comparisons.rs
+++ b/src/pages/api/comparisons.rs
@@ -1,6 +1,5 @@
-use std::collections::HashMap;
#[cfg(feature = "ssr")]
-use crate::pages::utils::server_utils::create_dynamo_client;
+use crate::utils::server::create_dynamo_client;
#[cfg(feature = "ssr")]
use aws_sdk_dynamodb::error::ProvideErrorMetadata;
@@ -12,6 +11,7 @@ use crate::common::{
ComparisonData, ComparisonType, MapListComparison, NumberComparison, NumberListComparison,
TextComparison, TextListComparison, ValueType,
};
+use std::collections::HashMap;
use leptos::prelude::ServerFnError;
use leptos::server;
diff --git a/src/pages/api/dates.rs b/src/pages/api/dates.rs
new file mode 100644
index 0000000..12d5cdf
--- /dev/null
+++ b/src/pages/api/dates.rs
@@ -0,0 +1,75 @@
+use std::collections::HashMap;
+use leptos::logging::log;
+use leptos::prelude::*;
+use crate::common::DateInfo;
+
+#[cfg(feature = "ssr")]
+static DATES_TABLE: &str = "leptos-dates";
+
+#[cfg(feature = "ssr")]
+use aws_sdk_dynamodb::{
+ types::{WriteRequest, PutRequest},
+ Client as DynamoClient
+};
+
+#[cfg(feature = "ssr")]
+use std::sync::Arc;
+
+#[server]
+pub async fn create_dates(dates: Vec) -> Result<(), ServerFnError> {
+ // Serialize and put dates into the database.
+ let client = expect_context::>();
+
+ log!("Creating dates: {:?}", dates);
+
+ let requests = dates
+ .into_iter()
+ .map(|date| {
+ let item = serde_dynamo::to_item(&date)
+ .unwrap_or_default();
+
+ let put_request = PutRequest::builder()
+ .set_item(Some(item))
+ .build()
+ .expect("Couldn't build request, item is not present");
+
+ WriteRequest::builder().put_request(put_request).build()
+ })
+ .collect::>();
+
+ let mut request_map = HashMap::new();
+ request_map.insert(DATES_TABLE.to_string(), requests);
+
+ client
+ .batch_write_item()
+ .set_request_items(Some(request_map))
+ .send()
+ .await
+ .map(|_| ())
+ .map_err(ServerFnError::from)
+}
+
+#[server]
+pub async fn get_important_dates() -> Result, ServerFnError> {
+ let client = expect_context::>();
+
+ client
+ .scan()
+ .table_name(DATES_TABLE)
+ .send()
+ .await
+ .map(|output| {
+ if let Some(items) = output.items {
+ items
+ .into_iter()
+ .filter_map(|item| {
+ serde_dynamo::from_item::<_, DateInfo>(item)
+ .ok()
+ })
+ .collect::>()
+ } else {
+ Vec::new()
+ }
+ })
+ .map_err(ServerFnError::from)
+}
diff --git a/src/pages/api/mod.rs b/src/pages/api/mod.rs
index e13ff2b..f7aa9bf 100644
--- a/src/pages/api/mod.rs
+++ b/src/pages/api/mod.rs
@@ -1,4 +1,7 @@
mod comparisons;
+pub use comparisons::*;
mod scholarships;
+pub use scholarships::*;
+mod dates;
+pub use dates::*;
-pub use self::{comparisons::*, scholarships::*};
diff --git a/src/pages/api/scholarships.rs b/src/pages/api/scholarships.rs
index d467a2e..6a8b345 100644
--- a/src/pages/api/scholarships.rs
+++ b/src/pages/api/scholarships.rs
@@ -8,7 +8,7 @@ use leptos::leptos_dom::log;
#[cfg(feature = "ssr")]
use uuid::Uuid;
#[cfg(feature = "ssr")]
-use crate::pages::utils::server_utils::create_dynamo_client;
+use crate::utils::server::{create_dynamo_client, into_attr_map};
#[cfg(feature = "ssr")]
use crate::common::ValueType;
@@ -52,7 +52,6 @@ pub async fn get_scholarship_info(id: String) -> Result Result<(), ServerFnError> {
- use crate::pages::utils::server_utils::create_dynamo_client;
use aws_sdk_dynamodb::error::ProvideErrorMetadata;
let client = create_dynamo_client().await;
diff --git a/src/pages/auth_callback.rs b/src/pages/auth_callback.rs
new file mode 100644
index 0000000..4b2dbe3
--- /dev/null
+++ b/src/pages/auth_callback.rs
@@ -0,0 +1,37 @@
+use leptos::prelude::*;
+use leptos_oidc::AuthSignal;
+use leptos_router::hooks::use_navigate;
+
+#[component]
+pub fn AuthCallbackPage() -> impl IntoView {
+ // This should wait for authentication, read the values in session storage, and then redirect
+ // to the correct page. The session storage should contain a path, which we'll need to check.
+
+ let auth = expect_context::();
+ let navigate = use_navigate();
+
+ Effect::new(move || {
+ // Wait for auth to load, then read information.
+ if let Some(_) = auth.get().authenticated() {
+ // Read the sessionStorage to get the last navigated path.
+ let storage_opt = window().session_storage().ok().flatten();
+ let path = storage_opt
+ .and_then(|storage| storage.get_item("post_login_path").ok().flatten())
+ .unwrap_or_else(|| "/".to_string());
+
+ // Clear storage in case we need to reauth again
+ if let Some(storage) = window().session_storage().ok().flatten() {
+ let _ = storage.remove_item("post_login_path");
+ }
+
+ // Redirect to the stored path
+ navigate(&path, Default::default());
+ }
+ });
+
+ view! {
+
+
"Completing the login process..."
+
+ }
+}
diff --git a/src/pages/comparison_test.rs b/src/pages/comparison_test.rs
index 8bb9ab8..50dd46f 100644
--- a/src/pages/comparison_test.rs
+++ b/src/pages/comparison_test.rs
@@ -1,16 +1,14 @@
use super::UnauthenticatedPage;
+use super::api::{get_all_scholarship_info, get_comparison_info};
use super::home_page::get_submission;
-use crate::common::{
- ComparisonData, ExpandableInfo, ValueType,
-};
+use crate::common::{ComparisonData, ExpandableInfo, 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::{AuthLoaded, Authenticated};
-use crate::pages::utils::get_user_claims;
-use super::api::{get_comparison_info, get_all_scholarship_info};
+use crate::utils::get_user_claims;
#[component]
pub fn ComparisonTestPage() -> impl IntoView {
@@ -59,93 +57,84 @@ pub fn ComparisonTestPage() -> impl IntoView {
let student_info = student_resource.get();
let comparison_info = comparison_resource.get();
let scholarship_info = scholarship_resource.get();
-
match (student_info, comparison_info, scholarship_info) {
- (Some(student), Some(comparisons), Some(scholarships)) => Either::Right(view! {
-
-
-
{result_msg}
-
-
- list,
- _ => {
- log!("Cannot find requirements, assuming none.");
- return Ok(Some(scholarship)) // students will always qualify when there are no requirements
- }
- };
+ (Some(student), Some(comparisons), Some(scholarships)) => {
+ Either::Right(
- 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 {
- ValueType::String(Some(comp_id)) => {
- // Find the comparison
- comparisons.iter().find(|comp| comp.id == *comp_id)
- }
- _ => None
- };
-
- let comparison_outcome = match comp_opt {
- Some(comp) => {
- // Perform the comparison
- comp.compare(&student)
- }
- _ => Err("".to_string())
- };
-
- match comparison_outcome {
- Ok(true) => {},
- Ok(false) => final_result = Ok(false),
- Err(e) => {
- final_result = Err(e);
- break;
+ view! {
+
+
+
{result_msg}
+
+
+ list,
+ _ => {
+ log!("Cannot find requirements, assuming none.");
+ return Ok(Some(scholarship));
+ }
+ };
+ let mut final_result = Ok(true);
+ for val in list {
+ let comp_opt = match val {
+ ValueType::String(Some(comp_id)) => {
+ comparisons.iter().find(|comp| comp.id == *comp_id)
+ }
+ _ => None,
+ };
+ let comparison_outcome = match comp_opt {
+ Some(comp) => comp.compare(&student),
+ _ => Err("".to_string()),
+ };
+ match comparison_outcome {
+ Ok(true) => {}
+ Ok(false) => final_result = Ok(false),
+ Err(e) => {
+ final_result = Err(e);
+ break;
+ }
+ }
+ }
+ match final_result {
+ Ok(true) => Ok(Some(scholarship)),
+ Ok(false) => Ok(None),
+ Err(e) => Err(e),
+ }
+ })
+ .collect::, String>>>();
+ let mut strings = Vec::new();
+ strings.push("Scholarship results: \n".to_string());
+ for result in comparison_results {
+ match result {
+ Ok(Some(scholarship_info)) => {
+ let name_member = "name".to_string();
+ let scl_name = scholarship_info
+ .data
+ .get(&name_member)
+ .unwrap()
+ .as_string()
+ .unwrap()
+ .unwrap();
+ strings.push(scl_name);
+ }
+ Ok(None) => {}
+ Err(msg) => strings.push(msg),
}
}
- }
-
- match final_result {
- Ok(true) => Ok(Some(scholarship)),
- Ok(false) => Ok(None),
- Err(e) => Err(e)
- }
- }).collect::, String>>>();
-
- // Build a display string
- let mut strings = Vec::new();
- strings.push("Scholarship results: \n".to_string());
- for result in comparison_results {
- match result {
- Ok(Some(scholarship_info)) => {
- // Get the scholarship name.
- let name_member = "name".to_string();
- let scl_name = scholarship_info.data.get(&name_member).unwrap()
- .as_string().unwrap().unwrap();
- strings.push(scl_name);
- },
- Ok(None) => {},
- Err(msg) => strings.push(msg)
- }
- }
-
- let display_text = strings.join("\n").to_string();
-
- result_msg.set(display_text);
- }>"Test Comparison"
-
-
- }),
- _ => Either::Left(view! {
-
+
+
}
}
diff --git a/src/pages/scholarship_info.rs b/src/pages/scholarship_info.rs
index b27953a..7a0628d 100644
--- a/src/pages/scholarship_info.rs
+++ b/src/pages/scholarship_info.rs
@@ -8,7 +8,7 @@ use leptos_router::hooks::{use_navigate, use_params};
use crate::common::{ExpandableInfo, ScholarshipFormParams, SubmitStatus, ValueType};
use crate::components::{ActionButton, Banner, ChipsList, Loading, OutlinedTextField, Panel, RadioList, Row, TextFieldType, Toast, ToastContext, ToastList, ValidatedForm};
use super::UnauthenticatedPage;
-use crate::pages::utils::get_user_claims;
+use crate::utils::get_user_claims;
use super::api::{get_provider_scholarships, get_scholarship_info, CreateScholarshipInfo, RegisterScholarship, DeleteProviderScholarship, get_comparisons_categorized, CreateTestComparisons};
@@ -609,7 +609,7 @@ fn ScholarshipForm(
items=vec![
"Write an essay describing your major/career direction. Include any abilities, interests, experiences, employment or clubs you have had that have affected your direction. If you are undecided, indicate why.",
"Why is community service important? How has it personally affected you and your goals and what role do you hope to play in it in the future?",
- "How has participation in sports been beneficial to your personal, academic, and emotional growth?"
+ "How has participation in sports been beneficial to your personal, academic, and emotional growth?",
]
.iter()
.map(|prompt| prompt.to_string())
diff --git a/src/pages/test_page.rs b/src/pages/test_page.rs
index f3e1f28..008676f 100644
--- a/src/pages/test_page.rs
+++ b/src/pages/test_page.rs
@@ -2,27 +2,44 @@ use crate::components::DashboardButton;
use leptos::prelude::*;
#[component]
-pub fn TestPage() -> impl IntoView {
+pub fn TestPage() -> impl IntoView {
view! {
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
Important Dates
+
+
+
+
+
+
+
+
+
- }
+ }.into_any()
}
diff --git a/src/pages/unauthenticated_page.rs b/src/pages/unauthenticated_page.rs
index 7de72e3..e2d6857 100644
--- a/src/pages/unauthenticated_page.rs
+++ b/src/pages/unauthenticated_page.rs
@@ -2,35 +2,43 @@ use crate::components::ActionButton;
use leptos::logging::log;
use leptos::prelude::*;
use leptos_oidc::AuthSignal;
+use leptos_router::hooks::{use_navigate, use_location};
#[component]
pub fn UnauthenticatedPage() -> impl IntoView {
let auth = use_context::().expect("AuthSignal not present in LoginLink");
- let login_url = move || {
+ let login_url = Memo::new(move |_| {
auth.with(|auth| {
auth.unauthenticated()
.map(|unauthenticated| unauthenticated.login_url())
})
+ });
+
+ Effect::new(move || log!("Generated login url: {:?}", login_url.get()));
+
+ let navigate = use_navigate();
+ let location = use_location();
+
+ let on_click = move |_| {
+ let current_path = location.pathname.get();
+
+ let storage_opt = window().session_storage().ok().flatten();
+ if let Some(session_storage) = storage_opt {
+ let _ = session_storage.set_item("post_login_path", ¤t_path);
+ }
+ if let Some(url) = login_url.get() {
+ navigate(&url, Default::default())
+ }
};
- Effect::new(move || log!("Generated login url: {:?}", login_url()));
-
- let navigate = leptos_router::hooks::use_navigate();
-
- view! (
+ view! {
-
"You're not signed in. Please sign in with your Region 15 email."
+
"You're not signed in. Please sign in with your Region 15 email."
-
- "Sign in"
-
+ "Sign in"
- )
+ }
}
diff --git a/src/pages/utils.rs b/src/pages/utils.rs
deleted file mode 100644
index 898ed2f..0000000
--- a/src/pages/utils.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-use leptos::prelude::{use_context, Signal, With};
-use leptos_oidc::{Algorithm, AuthSignal, TokenData};
-use crate::common::UserClaims;
-
-/// # 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).
-#[cfg(feature = "ssr")]
-pub mod server_utils {
- use std::collections::HashMap;
- use aws_config::SdkConfig;
- use crate::common::ValueType;
-
- /// Creates an [`SdkConfig`] struct for use with AWS SDK structs.
- 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)
- }
-
- pub fn into_attr_map(map: HashMap) -> HashMap {
- map.into_iter().map(|(k, v)| (k, v.into())).collect()
- }
-}
-
-/// Gets the current user claims. This function should only be used in an area that has
-/// access to an AuthSignal, or it will result in a total failure.
-pub fn get_user_claims() -> Signal