Create impromptu demo page #8

Merged
BobdaFett merged 1 commits from feature/multi-entry-data into main 2025-09-12 16:07:38 +00:00
14 changed files with 169 additions and 120 deletions

No files matched your search

Generated
+14 -14
View File
@@ -26,12 +26,6 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]] [[package]]
name = "android_system_properties" name = "android_system_properties"
version = "0.1.5" version = "0.1.5"
@@ -704,17 +698,16 @@ checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.41" version = "0.4.42"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
dependencies = [ dependencies = [
"android-tzdata",
"iana-time-zone", "iana-time-zone",
"js-sys", "js-sys",
"num-traits", "num-traits",
"serde", "serde",
"wasm-bindgen", "wasm-bindgen",
"windows-link", "windows-link 0.2.0",
] ]
[[package]] [[package]]
@@ -2945,6 +2938,7 @@ dependencies = [
"aws-config", "aws-config",
"aws-sdk-dynamodb", "aws-sdk-dynamodb",
"axum", "axum",
"chrono",
"console_error_panic_hook", "console_error_panic_hook",
"leptos", "leptos",
"leptos_axum", "leptos_axum",
@@ -3965,7 +3959,7 @@ checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
dependencies = [ dependencies = [
"windows-implement", "windows-implement",
"windows-interface", "windows-interface",
"windows-link", "windows-link 0.1.3",
"windows-result", "windows-result",
"windows-strings", "windows-strings",
] ]
@@ -3998,13 +3992,19 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
[[package]]
name = "windows-link"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
[[package]] [[package]]
name = "windows-result" name = "windows-result"
version = "0.3.4" version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
dependencies = [ dependencies = [
"windows-link", "windows-link 0.1.3",
] ]
[[package]] [[package]]
@@ -4013,7 +4013,7 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
dependencies = [ dependencies = [
"windows-link", "windows-link 0.1.3",
] ]
[[package]] [[package]]
@@ -4065,7 +4065,7 @@ version = "0.53.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
dependencies = [ dependencies = [
"windows-link", "windows-link 0.1.3",
"windows_aarch64_gnullvm 0.53.0", "windows_aarch64_gnullvm 0.53.0",
"windows_aarch64_msvc 0.53.0", "windows_aarch64_msvc 0.53.0",
"windows_i686_gnu 0.53.0", "windows_i686_gnu 0.53.0",
+1
View File
@@ -25,6 +25,7 @@ url = "2.5.4"
macros = { path = "./macros" } macros = { path = "./macros" }
traits = { path = "./traits" } traits = { path = "./traits" }
chrono = { version = "0.4.42", features = ["serde"] }
[features] [features]
hydrate = [ hydrate = [
+1 -1
View File
@@ -1,6 +1,6 @@
use proc_macro::TokenStream; use proc_macro::TokenStream;
use quote::quote; use quote::quote;
use syn::{parse_macro_input, Data, Ident}; use syn::{Data, Ident, parse_macro_input};
/// Derives the `AsReactive` and `ReactiveCapture` traits for a struct, while also /// Derives the `AsReactive` and `ReactiveCapture` traits for a struct, while also
/// providing a new reactive struct with the same field names, but they are wrapped /// providing a new reactive struct with the same field names, but they are wrapped
+4 -6
View File
@@ -1,11 +1,9 @@
use leptos::prelude::*; use leptos::prelude::*;
use leptos_meta::{provide_meta_context, MetaTags, Stylesheet, Title}; use leptos_meta::{MetaTags, Stylesheet, Title, provide_meta_context};
use leptos_oidc::{ use leptos_oidc::{Auth, AuthParameters, AuthSignal, Challenge, LoginLink};
Auth, AuthParameters, AuthSignal, Challenge, LoginLink,
};
use leptos_router::{ use leptos_router::{
components::{Route, Router, Routes},
StaticSegment, StaticSegment,
components::{Route, Router, Routes},
}; };
use url::Url; use url::Url;
@@ -47,7 +45,7 @@ pub fn App() -> impl IntoView {
let auth = Auth::signal(); let auth = Auth::signal();
provide_context(auth); // allows use of this signal in lower areas of the tree without 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); let _ = Auth::init(parameters);
+20
View File
@@ -1,6 +1,8 @@
use crate::components::MultiEntryData;
use macros::Reactive; use macros::Reactive;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::default::Default; use std::default::Default;
use chrono::{DateTime, Utc};
/// Represents information about a student. /// Represents information about a student.
#[derive(Serialize, Deserialize, Debug, Clone, Reactive, Default)] #[derive(Serialize, Deserialize, Debug, Clone, Reactive, Default)]
@@ -22,4 +24,22 @@ pub struct StudentInfo {
/// The student's desired contact email. /// The student's desired contact email.
#[serde(default)] #[serde(default)]
pub contact_email: String, pub contact_email: String,
/// The student's address.
#[serde(default)]
pub address: String,
/// The student's town of residence.
#[serde(default)]
pub town: String,
/// The student's entered phone number.
#[serde(default)]
pub phone_number: String, // May be stored as a straight number in the future.
/// The student's birthdate.
#[serde(default)]
pub birth_date: DateTime<Utc>,
/// The student's gender.
#[serde(default)]
pub gender: String,
/// The student's athletic information.
#[serde(default)]
pub athletic_info: Vec<MultiEntryData>,
} }
+3 -1
View File
@@ -45,9 +45,11 @@ pub fn CheckboxList(
#[prop(default = RwSignal::new(vec![]))] selected: RwSignal<Vec<String>>, #[prop(default = RwSignal::new(vec![]))] selected: RwSignal<Vec<String>>,
#[prop()] items: Vec<String>, #[prop()] items: Vec<String>,
#[prop(default = RwSignal::new(false))] disabled: RwSignal<bool>, #[prop(default = RwSignal::new(false))] disabled: RwSignal<bool>,
#[prop(optional, into)] label: String,
) -> impl IntoView { ) -> impl IntoView {
view! { view! {
<div> <div class="m-1.5 mt-0 mb-0">
<span class="font-bold">{label}</span>
{items {items
.into_iter() .into_iter()
.map(|item| { .map(|item| {
+4 -4
View File
@@ -87,14 +87,14 @@ pub fn Entry(
#[prop()] #[prop()]
info_member: Signal<MultiEntryMember>, info_member: Signal<MultiEntryMember>,
) -> impl IntoView { ) -> impl IntoView {
let nameData = entry_data.data.get(&name_member.get().member_name).unwrap(); let name_data = entry_data.data.get(&name_member.get().member_name).unwrap();
let infoData = entry_data.data.get(&info_member.get().member_name).unwrap(); let info_data = entry_data.data.get(&info_member.get().member_name).unwrap();
view! { view! {
// This div MUST have the same spacing CSS as the second-level header div // This div MUST have the same spacing CSS as the second-level header div
<div class="flex rounded-sm transition-shadow shadow-sm hover:shadow-lg/30 p-3 m-1"> <div class="flex rounded-sm transition-shadow shadow-sm hover:shadow-lg/30 p-3 m-1">
<span class="flex-1">{nameData.as_string()}</span> <span class="flex-1">{name_data.as_string()}</span>
<span class="flex-1">{infoData.as_string()}</span> <span class="flex-1">{info_data.as_string()}</span>
</div> </div>
} }
} }
+7 -5
View File
@@ -2,11 +2,11 @@ use leptos::prelude::*;
/// A panel for use in visual separation of a group of components. A `Panel` arranges /// A panel for use in visual separation of a group of components. A `Panel` arranges
/// a series of elements within a shaded rectangle using the flexbox column layout. /// a series of elements within a shaded rectangle using the flexbox column layout.
/// ///
/// It takes a series of children. Usually these would be in the form of a `Row` element, /// It takes a series of children. Usually these would be in the form of a `Row` element,
/// which will arrange its own children in a row within the panel, however it can be any /// which will arrange its own children in a row within the panel, however it can be any
/// HTML element. /// HTML element.
/// ///
/// Example: /// Example:
/// ``` /// ```
/// view! { /// view! {
@@ -24,14 +24,16 @@ use leptos::prelude::*;
/// ``` /// ```
#[component] #[component]
pub fn Panel( pub fn Panel(
children: Children #[prop(optional, default = false)] hidden: bool,
#[prop(optional)] children: Option<Children>
) -> impl IntoView { ) -> impl IntoView {
view! { view! {
<div <div
class="flex flex-col gap-2 p-2 m-4 flex-1 class="flex flex-col gap-2 p-2 m-4 flex-1
border-1 border-gray-600/10 border-1 border-gray-600/10
rounded-md shadow-[0_0_6px_rgba(0,0,0,0.50)]"> rounded-md shadow-[0_0_6px_rgba(0,0,0,0.50)]"
{children()} class=(["hidden"], hidden) >
{children.and_then(move |t| {Some(t())})}
</div> </div>
} }
} }
+3 -1
View File
@@ -45,9 +45,11 @@ pub fn RadioList(
#[prop(default = RwSignal::new(String::new()))] selected: RwSignal<String>, #[prop(default = RwSignal::new(String::new()))] selected: RwSignal<String>,
#[prop()] items: Vec<String>, #[prop()] items: Vec<String>,
#[prop(default = RwSignal::new(false))] disabled: RwSignal<bool>, #[prop(default = RwSignal::new(false))] disabled: RwSignal<bool>,
#[prop(optional, into)] label: String,
) -> impl IntoView { ) -> impl IntoView {
view! { view! {
<div> <div class="m-1.5 mt-0 mb-0">
<span class="font-bold">{label}</span>
{items {items
.into_iter() .into_iter()
.map(|item| { .map(|item| {
+1 -3
View File
@@ -1,9 +1,7 @@
use leptos::prelude::*; use leptos::prelude::*;
#[component] #[component]
pub fn Row( pub fn Row(children: Children) -> impl IntoView {
children: Children
) -> impl IntoView {
view! { view! {
<div class="flex flex-row gap-2 flex-1"> <div class="flex flex-row gap-2 flex-1">
{children()} {children()}
+5 -5
View File
@@ -8,15 +8,15 @@ use leptos::prelude::*;
pub fn Select( pub fn Select(
#[prop(default = vec!["Test Value".into()])] value_list: Vec<String>, #[prop(default = vec!["Test Value".into()])] value_list: Vec<String>,
#[prop(default = RwSignal::new("".into()))] value: RwSignal<String>, #[prop(default = RwSignal::new("".into()))] value: RwSignal<String>,
#[prop(default = RwSignal::new("".into()))] label: RwSignal<String>, #[prop(optional, into)] label: String,
#[prop(default = RwSignal::new(false))] disabled: RwSignal<bool>, #[prop(default = RwSignal::new(false))] disabled: RwSignal<bool>,
) -> impl IntoView { ) -> impl IntoView {
view! { view! {
<div> <div class="flex flex-1">
<label> <label class="flex flex-col flex-1">
{label} <span class="ml-1.5 mb-0 font-bold">{label}</span>
<select <select
class="relative border-2 m-1.5 p-1.5 rounded-md class="relative flex-1 border-2 m-1.5 mt-0 p-1.5 rounded-md
transition-border duration-150 transition-border duration-150
border-red-700 bg-transparent border-red-700 bg-transparent
disabled:border-gray-600 disabled:pointer-events-none disabled:bg-gray-600/33" disabled:border-gray-600 disabled:pointer-events-none disabled:bg-gray-600/33"
+18 -13
View File
@@ -1,30 +1,35 @@
use std::str::FromStr;
use leptos::prelude::*; use leptos::prelude::*;
use std::str::FromStr;
#[component] #[component]
pub fn OutlinedTextField<T>( pub fn OutlinedTextField<T>(
#[prop(default = T::default(), into)] placeholder: T, #[prop(optional, into)] placeholder: T,
#[prop(default = RwSignal::new(T::default()))] value: RwSignal<T>, #[prop(optional)] value: RwSignal<T>,
#[prop(default = RwSignal::new(false))] disabled: RwSignal<bool>, #[prop(optional)] disabled: RwSignal<bool>,
#[prop(default = String::from(""), into)] name: String, #[prop(optional)] error: RwSignal<bool>,
#[prop(default = String::from(""), into)] label: String, #[prop(optional, into)] name: String,
#[prop(default = String::from(""), into)] input_type: String, #[prop(optional, into)] label: String,
#[prop(optional, into)] input_type: String,
) -> impl IntoView ) -> impl IntoView
where where
T: Default + 'static + FromStr + ToString + PartialEq + Send + Sync + Clone T: Default + 'static + FromStr + ToString + PartialEq + Send + Sync + Clone,
{ {
// Parsing logic for non-string types which happens on each input. // Parsing logic for non-string types which happens on each input.
let on_input = move |e| { let on_input = move |e| {
let input_value = event_target_value(&e); let input_value = event_target_value(&e);
let parsed_value = T::from_str(&input_value) let can_parse = T::from_str(&input_value);
.unwrap_or_default(); if let Ok(parsed_value) = can_parse {
value.set(parsed_value); value.set(parsed_value);
} else {
error.set(true);
value.set(T::default());
}
}; };
view! ( view! (
<div class="flex flex-1"> <div class="flex flex-1">
<label class="flex flex-col flex-1"> <label class="flex flex-col flex-1">
<span class="block ml-1.5 mb-0">{label}</span> <span class="block ml-1.5 mb-0 font-bold">{label}</span>
<input <input
class="border-2 m-1.5 p-1.5 mt-0 rounded-md bg-transparent relative flex-1 class="border-2 m-1.5 p-1.5 mt-0 rounded-md bg-transparent relative flex-1
transition-all duration-150 transition-all duration-150
@@ -35,7 +40,7 @@ where
placeholder={placeholder.to_string()} placeholder={placeholder.to_string()}
prop:name=name prop:name=name
prop:value={move || value.get().to_string()} // Sets the initial value. prop:value={move || value.get().to_string()} // Sets the initial value.
on:input=on_input // Sets the value on each input. on:input=on_input
/> />
</label> </label>
</div> </div>
+1 -1
View File
@@ -4,7 +4,7 @@ async fn main() {
use axum::Router; use axum::Router;
use leptos::logging::log; use leptos::logging::log;
use leptos::prelude::*; use leptos::prelude::*;
use leptos_axum::{generate_route_list, LeptosRoutes}; use leptos_axum::{LeptosRoutes, generate_route_list};
use scholarships_rs::app::*; use scholarships_rs::app::*;
let conf = get_configuration(None).unwrap(); let conf = get_configuration(None).unwrap();
+87 -66
View File
@@ -1,16 +1,13 @@
// Server dependencies // Server dependencies
#[cfg(feature = "ssr")] #[cfg(feature = "ssr")]
use aws_sdk_dynamodb::{error::ProvideErrorMetadata, types::AttributeValue, Client}; use aws_sdk_dynamodb::{Client, error::ProvideErrorMetadata, types::AttributeValue};
#[cfg(feature = "ssr")] #[cfg(feature = "ssr")]
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::{StudentInfo, UserClaims}; use crate::common::{StudentInfo, UserClaims};
use crate::components::{ use crate::components::{ActionButton, CheckboxList, Loading, MultiEntry, MultiEntryMember, OutlinedTextField, OutlinedTextFieldPropsBuilder_Error_Repeated_field_disabled, Panel, RadioList, Row, Select};
ActionButton, CheckboxList, Loading, OutlinedTextField, Panel, Row, Select,
RadioList
};
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};
@@ -18,7 +15,6 @@ use traits::{AsReactive, ReactiveCapture};
#[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(id: String) -> Result<StudentInfo, 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);
@@ -36,11 +32,11 @@ pub async fn get_submission(id: String) -> Result<StudentInfo, ServerFnError> {
Ok(output) => { Ok(output) => {
if let Some(item) = output.item { if let Some(item) = output.item {
console_log(format!("Found item from API: {item:?}").as_str()); console_log(format!("Found item from API: {item:?}").as_str());
Ok(from_item(item).unwrap()) 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 mut info = StudentInfo::default();
info.Email = id; // Set the correct subject info.Email = id; // Set the correct subject
Ok(info) Ok(info)
} }
} }
@@ -52,10 +48,7 @@ pub async fn get_submission(id: String) -> Result<StudentInfo, ServerFnError> {
} }
#[server(CreateSampleSubmission, endpoint = "/create-sample-submission")] #[server(CreateSampleSubmission, endpoint = "/create-sample-submission")]
pub async fn create_sample_submission( pub async fn create_sample_submission(student_info: StudentInfo) -> Result<(), ServerFnError> {
student_info: StudentInfo,
) -> Result<(), 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);
@@ -68,19 +61,20 @@ pub async fn create_sample_submission(
.put_item() .put_item()
.table_name("student-applications") .table_name("student-applications")
.set_item(Some(item)) .set_item(Some(item))
.send().await .send()
.await
{ {
Ok(_) => { Ok(_) => {
// The student's information was successfully created // The student's information was successfully created
console_log(format!("Set up student information: {:?}", student_info).as_str()); console_log(format!("Set up student information: {:?}", student_info).as_str());
Ok(()) Ok(())
}, }
Err(err) => { Err(err) => {
// There was an error while creating the student's information // There was an error while creating the student's information
let msg = err.message().unwrap_or("An unknown error occurred."); let msg = err.message().unwrap_or("An unknown error occurred.");
console_log(format!("Error creating sample submission: {}", msg).as_str()); console_log(format!("Error creating sample submission: {}", msg).as_str());
Err(ServerFnError::new(msg)) Err(ServerFnError::new(msg))
}, }
} }
} }
@@ -103,14 +97,11 @@ pub fn HomePage() -> impl IntoView {
let server_resource = Resource::new( let server_resource = 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) Some(username) => get_submission(username).await.unwrap_or_else(|e| {
.await console_log(e.to_string().as_str());
.unwrap_or_else(|e| { StudentInfo::default()
console_log(e.to_string().as_str()); }),
StudentInfo::default() None => StudentInfo::default(),
}
),
None => StudentInfo::default()
}, },
); );
let submit_action = ServerAction::<CreateSampleSubmission>::new(); let submit_action = ServerAction::<CreateSampleSubmission>::new();
@@ -125,28 +116,38 @@ pub fn HomePage() -> impl IntoView {
.get() .get()
.map(|submission: StudentInfo| { .map(|submission: StudentInfo| {
let reactive_info = submission.as_reactive(); let reactive_info = submission.as_reactive();
let select_value = RwSignal::new(String::from("Math"));
let chk_select = RwSignal::new(vec!["Testing 2".into()]);
let elements_disabled = RwSignal::new(false); let elements_disabled = RwSignal::new(false);
let radio_value = RwSignal::new(String::from("Test")); 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! { view! {
<div class="flex flex-row"> <Row>
<div class="flex flex-col flex-1"/>
<Panel> <Panel>
<Row> <Row>
<p> <h1 class="text-3xl font-bold">
"Testing paragraph! This panel should be the same size as the other." "Region 15 Scholarship Application (DEMO)"
</p> </h1>
</Row> </Row>
<Row> <Row>
<p>"Current user's subject ID: "{reactive_info.Email}</p> <p class="text-sm">
</Row> "This is a very simple demonstration of the scholarship application written in Rust."
</Panel>
<Panel>
<Row>
<p>
"Current user's reported full name from the API: "
{reactive_info.first_name}" " {reactive_info.last_name}
</p> </p>
</Row> </Row>
<Row> <Row>
@@ -162,33 +163,36 @@ pub fn HomePage() -> impl IntoView {
disabled=elements_disabled disabled=elements_disabled
value=reactive_info.last_name value=reactive_info.last_name
/> />
</Row>
<Row>
<OutlinedTextField <OutlinedTextField
label="Contact Email" label="Contact Email:"
placeholder="temp@region15.org" placeholder="student@region15.org"
disabled=elements_disabled disabled=elements_disabled
value=reactive_info.contact_email value=reactive_info.contact_email
/> />
</Row> </Row>
<Row> <Row>
<Select <OutlinedTextField
value_list=vec!["Math", "English", "Science"] label="Phone Number:"
.into_iter() placeholder="123-456-7890"
.map(|s| s.into())
.collect()
value=select_value
disabled=elements_disabled disabled=elements_disabled
value=reactive_info.phone_number
/> />
</Row>
<Row>
<OutlinedTextField
label="Street Address:"
placeholder="123 Fake Street"
disabled=elements_disabled
value=reactive_info.address
/>
</Row>
<Row>
<RadioList <RadioList
selected=radio_value label="Town:"
items=vec!["Test", "Testing 2"] selected=reactive_info.town
.into_iter() items=vec!["Southbury", "Middlebury"]
.map(|s| s.into())
.collect()
disabled=elements_disabled
/>
<CheckboxList
selected=chk_select
items=vec!["Testing 1", "Testing 2"]
.into_iter() .into_iter()
.map(|s| s.into()) .map(|s| s.into())
.collect() .collect()
@@ -196,18 +200,35 @@ pub fn HomePage() -> impl IntoView {
/> />
</Row> </Row>
<Row> <Row>
<ActionButton on:click=move |_| { <Select
console_log( label="Gender:"
format!("Found value {:?}", radio_value.get()).as_str(), value_list=vec!["Male", "Female", "Prefer not to answer"]
); .into_iter()
submit_action .map(|s| s.into())
.dispatch(CreateSampleSubmission { .collect()
student_info: reactive_info.capture(), value=reactive_info.gender
}); disabled=elements_disabled
}>"Submit"</ActionButton> />
</Row>
<Row>
<ActionButton
on:click=move |_| {
submit_action
.dispatch(CreateSampleSubmission {
student_info: reactive_info.capture(),
});
}
disabled=elements_disabled
>"Submit"</ActionButton>
</Row>
<Row>
<p>
{result_msg}
</p>
</Row> </Row>
</Panel> </Panel>
</div> <div class="flex flex-col flex-1"/>
</Row>
} }
}) })
}} }}