Refactor all components to use expandable data #10
No files matched your search
@@ -15,19 +15,19 @@ use crate::components::ValueType;
|
|||||||
/// now, the table will simply create a single massive `Map` field that contains all the stored
|
/// 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,
|
/// 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.
|
/// especially for interoperability with other applications, should they be created.
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, Reactive)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Reactive)]
|
||||||
pub struct ExpandableInfo {
|
pub struct ExpandableInfo {
|
||||||
/// The main key member. Required and used to get/put the information in a database.
|
/// The main key member. Required and used to get/put the information in a database.
|
||||||
key_member: String,
|
pub subject: String,
|
||||||
/// The main data store. Contains all information that this struct needs to hold.
|
/// The main data store. Contains all information that this struct needs to hold.
|
||||||
#[serde(default)]
|
#[serde(default, flatten)]
|
||||||
data: HashMap<String, ValueType>,
|
pub data: HashMap<String, ValueType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExpandableInfo {
|
impl ExpandableInfo {
|
||||||
pub fn new(key: String) -> Self {
|
pub fn new(key: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
key_member: key,
|
subject: key,
|
||||||
data: HashMap::new(),
|
data: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ pub enum ValueType {
|
|||||||
/// Indicates a `String` value.
|
/// Indicates a `String` value.
|
||||||
String(Option<String>),
|
String(Option<String>),
|
||||||
/// Indicates an `i32` value.
|
/// Indicates an `i32` value.
|
||||||
Number(Option<i32>),
|
Number(Option<String>),
|
||||||
/// Indicates a `Vec<T>`, where `T` can be any other `ValueType`.
|
/// Indicates a `Vec<T>`, where `T` can be any other `ValueType`.
|
||||||
List(Option<Vec<ValueType>>),
|
List(Option<Vec<ValueType>>),
|
||||||
}
|
}
|
||||||
@@ -35,7 +35,7 @@ impl ValueType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to get this value as an `i32`.
|
/// Attempts to get this value as an `i32`.
|
||||||
pub fn as_number(&self) -> Result<Option<i32>, Self> {
|
pub fn as_number(&self) -> Result<Option<String>, Self> {
|
||||||
if let ValueType::Number(v) = self {
|
if let ValueType::Number(v) = self {
|
||||||
Ok(v.clone())
|
Ok(v.clone())
|
||||||
} else {
|
} else {
|
||||||
@@ -69,7 +69,7 @@ impl Display for ValueType {
|
|||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let str = match self {
|
let str = match self {
|
||||||
ValueType::String(s) => s.clone().unwrap_or("".to_owned()),
|
ValueType::String(s) => s.clone().unwrap_or("".to_owned()),
|
||||||
ValueType::Number(n) => n.unwrap_or(0).to_string(),
|
ValueType::Number(n) => n.clone().unwrap_or("".to_owned()),
|
||||||
ValueType::List(l) => format!("{l:?}"),
|
ValueType::List(l) => format!("{l:?}"),
|
||||||
};
|
};
|
||||||
write!(f, "{}", str)
|
write!(f, "{}", str)
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ pub fn OutlinedTextField(
|
|||||||
// If the number parses, Some(num), else None, but we want to keep the value that's
|
// If the number parses, Some(num), else None, but we want to keep the value that's
|
||||||
// in the text field at this point, just set the error.
|
// in the text field at this point, just set the error.
|
||||||
console_log(format!("Parsing {to_parse} to a number").as_str());
|
console_log(format!("Parsing {to_parse} to a number").as_str());
|
||||||
match to_parse.parse::<i32>().map(|n| ValueType::Number(Some(n))) {
|
match to_parse.parse::<i32>().map(|n| ValueType::Number(Some(n.to_string()))) {
|
||||||
Ok(value_type) => {
|
Ok(value_type) => {
|
||||||
error.set(false);
|
error.set(false);
|
||||||
value.set(value_type.clone());
|
value.set(value_type.clone());
|
||||||
|
|||||||
+19
-27
@@ -7,12 +7,11 @@ use aws_sdk_dynamodb::{Client, error::ProvideErrorMetadata, types::AttributeValu
|
|||||||
use serde_dynamo::{from_item, to_item};
|
use serde_dynamo::{from_item, to_item};
|
||||||
|
|
||||||
use crate::app::Unauthenticated;
|
use crate::app::Unauthenticated;
|
||||||
use crate::common::{ExpandableInfo, StudentInfo, UserClaims};
|
use crate::common::{ExpandableInfo, UserClaims};
|
||||||
use crate::components::{ActionButton, CheckboxList, Loading, OutlinedTextField, Panel, RadioList, Row, Select, ValueType};
|
use crate::components::{ActionButton, CheckboxList, Loading, OutlinedTextField, Panel, RadioList, Row, Select};
|
||||||
use leptos::leptos_dom::logging::console_log;
|
use leptos::leptos_dom::logging::console_log;
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use leptos_oidc::{Algorithm, AuthLoaded, AuthSignal, Authenticated};
|
use leptos_oidc::{Algorithm, AuthLoaded, AuthSignal, Authenticated};
|
||||||
use reactive_stores::Store;
|
|
||||||
use traits::{AsReactive, ReactiveCapture};
|
use traits::{AsReactive, ReactiveCapture};
|
||||||
|
|
||||||
/// # Get Student Info
|
/// # Get Student Info
|
||||||
@@ -20,18 +19,18 @@ use traits::{AsReactive, ReactiveCapture};
|
|||||||
///
|
///
|
||||||
/// All information is found by using a `GetItemCommand` in the student application DynamoDB table.
|
/// All information is found by using a `GetItemCommand` in the student application DynamoDB table.
|
||||||
#[server(GetSubmission, endpoint = "/get-submission")]
|
#[server(GetSubmission, endpoint = "/get-submission")]
|
||||||
pub async fn get_submission(id: String) -> Result<StudentInfo, ServerFnError> {
|
pub async fn get_submission(subject: String) -> Result<ExpandableInfo, ServerFnError> {
|
||||||
let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
|
let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
|
||||||
let dbclient = Client::new(&config);
|
let dbclient = Client::new(&config);
|
||||||
|
|
||||||
console_log(format!("Getting values from API using username {}", id).as_str());
|
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
|
// Gets the item from the database. It only returns an error if the database
|
||||||
// experiences an error - not if the item is not found.
|
// experiences an error - not if the item is not found.
|
||||||
match dbclient
|
match dbclient
|
||||||
.get_item()
|
.get_item()
|
||||||
.table_name("student-applications")
|
.table_name("leptos-test")
|
||||||
.key("Email", AttributeValue::S(id.clone()))
|
.key("subject", AttributeValue::S(subject.clone()))
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
@@ -41,8 +40,7 @@ pub async fn get_submission(id: String) -> Result<StudentInfo, ServerFnError> {
|
|||||||
Ok(from_item(item)?)
|
Ok(from_item(item)?)
|
||||||
} else {
|
} else {
|
||||||
console_log("Couldn't find entry, returning default.");
|
console_log("Couldn't find entry, returning default.");
|
||||||
let mut info = StudentInfo::default();
|
let info = ExpandableInfo::new(subject);
|
||||||
info.Email = id; // Set the correct subject
|
|
||||||
Ok(info)
|
Ok(info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,7 +67,7 @@ pub async fn create_sample_submission(student_info: ExpandableInfo) -> Result<()
|
|||||||
console_log(format!("Arguments for sample submission: {:?}", item).as_str());
|
console_log(format!("Arguments for sample submission: {:?}", item).as_str());
|
||||||
match dbclient
|
match dbclient
|
||||||
.put_item()
|
.put_item()
|
||||||
.table_name("student-applications")
|
.table_name("leptos-test")
|
||||||
.set_item(Some(item))
|
.set_item(Some(item))
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
@@ -92,7 +90,7 @@ pub async fn create_sample_submission(student_info: ExpandableInfo) -> Result<()
|
|||||||
pub async fn log_expandable(info: ExpandableInfo) -> Result<(), ServerFnError> {
|
pub async fn log_expandable(info: ExpandableInfo) -> Result<(), ServerFnError> {
|
||||||
console_log(format!("Was given info: {:?}", info).as_str());
|
console_log(format!("Was given info: {:?}", info).as_str());
|
||||||
// Manually specify the type here since we're not able to infer the type later.
|
// Manually specify the type here since we're not able to infer the type later.
|
||||||
let item: HashMap<String, AttributeValue> = to_item(&info)?;
|
let item: HashMap<String, AttributeValue> = to_item(info)?;
|
||||||
console_log(format!("Converted into a DynamoDB item: {:?}", item).as_str());
|
console_log(format!("Converted into a DynamoDB item: {:?}", item).as_str());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -115,14 +113,14 @@ pub fn HomePage() -> impl IntoView {
|
|||||||
// Note that the value passed in MUST be equatable.
|
// 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 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.
|
// we don't lose access to it in the future, should we need it again.
|
||||||
let server_resource: Resource<StudentInfo> = Resource::new(
|
let server_resource: Resource<ExpandableInfo> = Resource::new(
|
||||||
move || user_claims.get().map(|claim| claim.claims.subject.clone()),
|
move || user_claims.get().map(|claim| claim.claims.subject.clone()),
|
||||||
async |opt_username| match opt_username {
|
async |opt_username| match opt_username {
|
||||||
Some(username) => get_submission(username).await.unwrap_or_else(|e| {
|
Some(subject) => get_submission(subject.clone()).await.unwrap_or_else(|e| {
|
||||||
console_log(e.to_string().as_str());
|
console_log(e.to_string().as_str());
|
||||||
StudentInfo::default()
|
ExpandableInfo::new(subject)
|
||||||
}),
|
}),
|
||||||
None => StudentInfo::default(),
|
None => ExpandableInfo::new("".to_owned()),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let submit_action = ServerAction::<CreateSampleSubmission>::new();
|
let submit_action = ServerAction::<CreateSampleSubmission>::new();
|
||||||
@@ -137,13 +135,7 @@ pub fn HomePage() -> impl IntoView {
|
|||||||
server_resource
|
server_resource
|
||||||
.get()
|
.get()
|
||||||
.map(|submission| {
|
.map(|submission| {
|
||||||
let expandable = ExpandableInfo::new("email".into());
|
let expandable_react = submission.as_reactive();
|
||||||
let expandable_react = expandable.as_reactive();
|
|
||||||
expandable_react.data.update(|map| {
|
|
||||||
map.insert("email".into(), ValueType::String(Some("test@region15.org".into())));
|
|
||||||
map.insert("phone_number".into(), ValueType::String(Some("123456789-10".into())));
|
|
||||||
map.insert("math_sat".into(), ValueType::Number(Some(1234)));
|
|
||||||
});
|
|
||||||
let elements_disabled = RwSignal::new(false);
|
let elements_disabled = RwSignal::new(false);
|
||||||
let result_msg = Signal::derive(move || {
|
let result_msg = Signal::derive(move || {
|
||||||
// Eventually, this function will show and hide a loading symbol
|
// Eventually, this function will show and hide a loading symbol
|
||||||
@@ -246,6 +238,7 @@ pub fn HomePage() -> impl IntoView {
|
|||||||
/>
|
/>
|
||||||
</Row>
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
|
// Checkbox { data_member, label, items }
|
||||||
<CheckboxList
|
<CheckboxList
|
||||||
label="Favorite Candies:"
|
label="Favorite Candies:"
|
||||||
items=vec!["Twizzlers", "Reese's", "Starburst"]
|
items=vec!["Twizzlers", "Reese's", "Starburst"]
|
||||||
@@ -263,7 +256,7 @@ pub fn HomePage() -> impl IntoView {
|
|||||||
label="Gender:"
|
label="Gender:"
|
||||||
value_list=vec!["Male", "Female", "Prefer not to answer"]
|
value_list=vec!["Male", "Female", "Prefer not to answer"]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|s| s.into())
|
.map(|s| s.to_owned())
|
||||||
.collect()
|
.collect()
|
||||||
disabled=elements_disabled
|
disabled=elements_disabled
|
||||||
data_member="gender"
|
data_member="gender"
|
||||||
@@ -275,10 +268,9 @@ pub fn HomePage() -> impl IntoView {
|
|||||||
on:click=move |_| {
|
on:click=move |_| {
|
||||||
let captured_map = expandable_react.capture();
|
let captured_map = expandable_react.capture();
|
||||||
console_log(format!("Map values: {:?}", captured_map).as_str());
|
console_log(format!("Map values: {:?}", captured_map).as_str());
|
||||||
submit_action
|
submit_action.dispatch(CreateSampleSubmission {
|
||||||
.dispatch(CreateSampleSubmission {
|
student_info: captured_map,
|
||||||
student_info: captured_map,
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
disabled=elements_disabled
|
disabled=elements_disabled
|
||||||
>"Submit"</ActionButton>
|
>"Submit"</ActionButton>
|
||||||
|
|||||||
Reference in new issue
Block a user