feat: create impromptu demo page
This commit is contained in:
1 parent
8c46ad5dfd
commit
4fc01acd50
14 files changed
+169
-120
No files matched your search
Generated
+14
-14
@@ -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",
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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>,
|
||||||
}
|
}
|
||||||
@@ -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| {
|
||||||
|
|||||||
@@ -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>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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,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()}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
|
|||||||
Reference in new issue
Block a user