diff --git a/src/pages/api/mod.rs b/src/pages/api/mod.rs
index e30afcb..5e63eda 100644
--- a/src/pages/api/mod.rs
+++ b/src/pages/api/mod.rs
@@ -2,11 +2,11 @@ mod admin;
mod comparisons;
mod dates;
pub mod exports;
+pub mod files;
mod providers;
mod scholarships;
pub mod students;
-pub mod files;
-mod tokens;
+pub(crate) mod tokens;
pub use admin::*;
pub use comparisons::*;
diff --git a/src/pages/provider_applicants.rs b/src/pages/provider_applicants.rs
index 181e907..84fe9a7 100644
--- a/src/pages/provider_applicants.rs
+++ b/src/pages/provider_applicants.rs
@@ -1,5 +1,8 @@
-use leptos::prelude::*;
-use crate::components::Banner;
+use crate::common::ValueType;
+use crate::components::{Banner, Loading};
+use leptos::either::Either;
+use leptos::prelude::*;
+use leptos_oidc::AuthSignal;
#[component]
pub fn ApplicantsPageFallback() -> impl IntoView {
@@ -14,4 +17,112 @@ pub fn ApplicantsPageFallback() -> impl IntoView {
"Come back soon!"
}
-}
\ No newline at end of file
+}
+
+#[component]
+pub fn ApplicantsPageShell() -> impl IntoView {
+ // This shell needs to simply contain everything that will stay consistent across paths.
+ // The path will change to contain each scholarship's id, which will directly affect the content
+ // shown in a subroute for this page.
+
+ // As a result, this page likely only needs to contain the banner and the list of scholarships.
+ // The content will be shown within the right side, but only as a result of some subroutes,
+ // each of which is still considered a single page.
+
+ view! { }
+}
+
+#[component]
+fn ApplicantsScholarshipList() -> impl IntoView {
+ use crate::common::ExpandableInfo;
+
+ #[server]
+ async fn get_provider_scholarships(
+ access_token: String,
+ ) -> Result, ServerFnError> {
+ use crate::pages::api::SCHOLARSHIPS_TABLE;
+ use crate::pages::api::tokens::validate_and_get_token_info;
+
+ let claims = validate_and_get_token_info(access_token).await?;
+
+ // Get the information from the database.
+ let client = crate::utils::server::create_dynamo_client().await;
+
+ let output = client
+ .scan()
+ .table_name(SCHOLARSHIPS_TABLE)
+ .expression_attribute_values(
+ ":id",
+ serde_dynamo::to_attribute_value(ValueType::String(Some(claims.subject)))?,
+ )
+ .filter_expression("provider_id = :id")
+ .send()
+ .await?
+ .items
+ .unwrap_or_default()
+ .into_iter()
+ .filter_map(|item| serde_dynamo::from_item(item).ok())
+ .collect::>();
+
+ Ok(output)
+ }
+
+ // Use access token for user identification
+ let auth = expect_context::();
+ let access_token =
+ Memo::new(move |_| auth.with(|auth| auth.authenticated().map(|a| a.access_token())));
+
+ // We need to get the scholarships from the API. We don't need the whole scholarship, just the
+ // name and ID.
+ let trigger = Trigger::new();
+ let scholarships_res = Resource::new(
+ move || (trigger.track(), access_token.get()),
+ async move |(_, access_token)| {
+ get_provider_scholarships(access_token.unwrap_or_default()).await
+ },
+ );
+
+ view! {
+
+
"Scholarships"
+
+ {move || {
+ scholarships_res
+ .get()
+ .map(|items_res| {
+ let items = match items_res {
+ Ok(items) => items,
+ Err(e) => {
+ return Either::Left(
+ view! {
+
+ {format!("Couldn't get scholarships: {}", e.to_string())}
+
+ }
+ .into_any(),
+ );
+ }
+ };
+ Either::Right(
+ items
+ .into_iter()
+ .map(|item| {
+
+ view! { }
+ .into_any()
+ })
+ .collect_view(),
+ )
+ })
+ .collect_view()
+ }}
+
+
+ }
+}
+
+#[component]
+fn ApplicantsScholarshipEntry() -> impl IntoView {}
+
+#[component]
+fn ApplicantsStudentList() -> impl IntoView {}