use std::collections::HashMap; // Server dependencies #[cfg(feature = "ssr")] use aws_sdk_dynamodb::{Client, error::ProvideErrorMetadata, types::AttributeValue}; #[cfg(feature = "ssr")] use serde_dynamo::{from_item, to_item}; use crate::common::{ExpandableInfo, UserClaims}; use crate::components::{ActionButton, CheckboxList, Loading, OutlinedTextField, Panel, RadioList, Row, Select}; use crate::pages::{UnauthenticatedPage}; use leptos::leptos_dom::logging::console_log; use leptos::prelude::*; use leptos_oidc::{Algorithm, AuthLoaded, AuthSignal, Authenticated}; use traits::{AsReactive, ReactiveCapture}; /// # Get Student Info /// Gets a student's information given their `subject`. /// /// All information is found by using a `GetItemCommand` in the student application DynamoDB table. #[server(GetSubmission, endpoint = "/get-submission")] pub async fn get_submission(subject: String) -> Result { let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; let dbclient = Client::new(&config); console_log(format!("Getting values from API using subject {}", subject).as_str()); // Gets the item from the database. It only returns an error if the database // experiences an error - not if the item is not found. match dbclient .get_item() .table_name("leptos-test") .key("subject", AttributeValue::S(subject.clone())) .send() .await { Ok(output) => { if let Some(item) = output.item { console_log(format!("Found item from API: {item:?}").as_str()); Ok(from_item(item)?) } else { console_log("Couldn't find entry, returning default."); let info = ExpandableInfo::new(subject); Ok(info) } } Err(err) => { let msg = err.message().unwrap_or("Unknown error"); Err(ServerFnError::new(msg)) } } } /// # Create Sample Submission /// Creates a submission given a full `StudentInfo` struct. /// /// All information is stored using a `PutItemCommand` in the student application DynamoDB table. #[server(CreateSampleSubmission, endpoint = "/create-sample-submission")] pub async fn create_sample_submission(student_info: ExpandableInfo) -> Result<(), ServerFnError> { let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; let dbclient = Client::new(&config); // The put_item action can create or update an item in a DynamoDB table. // It will completely replace any existing item with the same primary key, // which is the desired behavior. let item = to_item(student_info.clone())?; console_log(format!("Arguments for sample submission: {:?}", item).as_str()); match dbclient .put_item() .table_name("leptos-test") .set_item(Some(item)) .send() .await { Ok(_) => { // The student's information was successfully created console_log(format!("Set up student information: {:?}", student_info).as_str()); Ok(()) } Err(err) => { // There was an error while creating the student's information let msg = err.message().unwrap_or("An unknown error occurred."); console_log(format!("Error creating sample submission: {}", msg).as_str()); Err(ServerFnError::new(msg)) } } } #[server(LogExpandableInfo, endpoint = "/log-expandable")] pub async fn log_expandable(info: ExpandableInfo) -> Result<(), ServerFnError> { console_log(format!("Was given info: {:?}", info).as_str()); // Manually specify the type here since we're not able to infer the type later. let item: HashMap = to_item(info)?; console_log(format!("Converted into a DynamoDB item: {:?}", item).as_str()); Ok(()) } /// The main home page component. Contains a simple contact form. #[component] pub fn HomePage() -> impl IntoView { // Creates a reactive value to update the button let auth = use_context::().expect("Couldn't find user information."); let user_claims = Signal::derive(move || { auth.with(|auth| { auth.authenticated().and_then(|data| { data.decoded_access_token::(Algorithm::RS256, &["account"]) }) }) }); // Note that the value passed in MUST be equatable. // We get/unwrap the value repeatedly until we get a simple string value, then clone it so that // we don't lose access to it in the future, should we need it again. let server_resource: Resource = 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()), }, ); let submit_action = ServerAction::::new(); let log_action = ServerAction::::new(); view! { // Replace this fallback with a real loading screen. {move || { server_resource .get() .map(|submission| { let expandable_react = submission.as_reactive(); let elements_disabled = RwSignal::new(false); let result_msg = Signal::derive(move || { // Eventually, this function will show and hide a loading symbol // and a success checkmark. if submit_action.pending().get() { elements_disabled.set(true); "Sending request...".to_owned() } else { elements_disabled.set(false); if let Some(result) = submit_action.value().get() { match result { Ok(_) => "Request sent successfully.".to_owned(), Err(e) => format!("Failed to send request: {:?}", e) } } else { "".to_owned() } } }); view! {

"Region 15 Scholarship Application (DEMO)"

"This is a very simple demonstration of the scholarship application written in Rust."

// Text { data_member, label, placeholder } // Radio { data_member, label, items } // Checkbox { data_member, label, items } // Select { data_member, label, items }