chore: pc change

This commit is contained in:
alphafetus committed 2025-08-26 09:57:58 -04:00
1 parent 21ba5e45ff
commit f7ca1ecbc5
27 files changed
+926 -262

No files matched your search

+46 -5
View File
@@ -1,15 +1,15 @@
use leptos::prelude::*;
use leptos_meta::{provide_meta_context, MetaTags, Stylesheet, Title};
use leptos_oidc::{
Auth, AuthLoaded, AuthParameters, AuthSignal, Authenticated, Challenge, LoginLink, LogoutLink,
};
use leptos_router::{
components::{Route, Router, Routes},
StaticSegment,
};
use url::Url;
use crate::pages::{
AboutPage,
HomePage,
ProviderEntry
};
use crate::pages::{AboutPage, HomePage, ProviderEntry};
pub fn shell(options: LeptosOptions) -> impl IntoView {
view! {
@@ -34,17 +34,38 @@ pub fn App() -> impl IntoView {
// Provides context that manages stylesheets, titles, meta tags, etc.
provide_meta_context();
// Authentication setup
let parameters = AuthParameters {
issuer: "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Lfjuy5zaM".into(),
client_id: "10jr2h3vtpu9n7gj46pvg5qo2q".into(),
redirect_uri: Url::parse("http://localhost:3000").unwrap().to_string(),
post_logout_redirect_uri: Url::parse("http://localhost:3000").unwrap().to_string(),
scope: Some("openid%20profile%20email".into()),
audience: None,
challenge: Challenge::None,
};
let auth = Auth::signal();
provide_context(auth); // allows use of this signal in lower areas of the tree without
// explicitly passing it through the html tree
let _ = Auth::init(parameters);
view! {
// injects a stylesheet into the document <head>
// id=leptos means cargo-leptos will hot-reload this stylesheet
<Stylesheet id="leptos" href="/pkg/scholarships-rs-wasm.css"/>
// <AuthLoading><p>"Authentication is loading"</p></AuthLoading>
// <AuthErrorContext><AuthErrorView/></AuthErrorContext>
// sets the document title
<Title text="Welcome to Leptos"/>
// content for this welcome page
<Router>
<main>
// TODO Create a 404 page
<Routes fallback=|| "Page not found.".into_view()>
<Route path=StaticSegment("") view=HomePage/>
<Route path=StaticSegment("about") view=AboutPage/>
@@ -54,3 +75,23 @@ pub fn App() -> impl IntoView {
</Router>
}
}
#[component]
pub fn Unauthenticated() -> impl IntoView {
view! {
<p>"Error 401: Unauthorized"</p>
<LoginLink class="text-login">"Sign in"</LoginLink>
}
}
#[component]
pub fn AuthErrorView() -> impl IntoView {
let auth = use_context::<AuthSignal>().expect("AuthError: RwSignal<AuthStore> was not found");
let error_message = move || auth.get().error().map(|error| format!("{error:?}"));
view! {
<h1>"An authentication error occurred."</h1>
{error_message}
}
}
+6 -9
View File
@@ -1,15 +1,14 @@
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct FormDefinition {
sections: Vec<Section>
sections: Vec<Section>,
}
#[derive(Serialize, Deserialize)]
struct Section {
questions: Vec<QuestionType>
questions: Vec<QuestionType>,
}
#[derive(Serialize, Deserialize)]
@@ -20,12 +19,10 @@ enum QuestionType {
Dropdown,
Checkbox,
Radio,
Multi
Multi,
}
struct TextInput {
}
struct TextInput {}
#[derive(Serialize, Deserialize)]
struct QuestionComponent {
@@ -34,5 +31,5 @@ struct QuestionComponent {
placeholder: Option<String>,
// Used in <select> and <checkbox> elements
options: Option<HashMap<String, String>>
options: Option<HashMap<String, String>>,
}
+2 -3
View File
@@ -1,10 +1,9 @@
#[derive(Clone)]
pub struct LoanerUser {
pub first_name: String,
pub last_name: String,
pub email: String,
pub has_loan: bool
pub has_loan: bool,
}
impl LoanerUser {
@@ -13,7 +12,7 @@ impl LoanerUser {
first_name: "".to_string(),
last_name: "".to_string(),
email: "".to_string(),
has_loan: false
has_loan: false,
}
}
}
+4 -6
View File
@@ -1,8 +1,6 @@
mod loaner_user;
mod student_info;
mod form_definition;
mod loaner_user;
mod student_info;
mod user;
pub use self::{
loaner_user::*,
student_info::*
};
pub use self::{loaner_user::*, student_info::*, user::*};
+4 -2
View File
@@ -1,7 +1,9 @@
use serde::{Deserialize, Serialize};
use macros::Reactive;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, Reactive)]
pub struct StudentInfo {
pub first_name: String,
pub last_name: String,
pub math_sat_score: i32,
}
+25
View File
@@ -0,0 +1,25 @@
use serde::{Deserialize, Serialize};
// API URL to get user information from Google's Groups API:
// https://admin.googleapis.com/admin/directory/v1/groups?domain=region15.org&userKey=<email>
// Note that this requires a service account of some type, and that it should only be called from
// a pre-sign up lambda function.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserClaims {
/// The time the JWT was issued, in UTC time.
#[serde(rename = "iat")]
issued_at: usize,
/// The issuer of the JWT.
#[serde(rename = "iss")]
issuer: String,
/// The time the JWT expires, in UTC time.
#[serde(rename = "exp")]
expiration_at: usize,
/// The subject of the JWT. This is the only unique identifier for a user.
#[serde(rename = "sub")]
pub subject: String,
/// The username of the user. This should be their email address.
#[serde(rename = "username")]
pub username: String,
}
+19
View File
@@ -0,0 +1,19 @@
use leptos::prelude::*;
#[component]
pub fn Checkbox(
#[prop()] checked: ReadSignal<bool>,
#[prop()] on_change: Callback<bool>,
#[prop()] label: String,
#[prop()] id: String
) -> impl IntoView {
let label_id = id.clone();
view! {
<label for=label_id>
<input
type="checkbox"
id={id}
/>
</label>
}
}
+10
View File
@@ -0,0 +1,10 @@
use leptos::prelude::*;
#[component]
pub fn Loading() -> impl IntoView {
view! {
<div>
<img src="loading.gif" />
</div>
}
}
+5 -6
View File
@@ -1,9 +1,8 @@
mod action_button;
mod text_field;
mod loading;
mod multi_entry;
mod text_field;
mod select;
mod checkboxes;
pub use self::{
action_button::*,
text_field::*,
multi_entry::*
};
pub use self::{action_button::*, loading::*, multi_entry::*, text_field::*, select::*};
+14 -15
View File
@@ -1,9 +1,9 @@
use std::collections::HashMap;
use crate::components::ActionButton;
use leptos::{leptos_dom::logging::console_log, prelude::*};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::components::ActionButton;
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct MultiEntryData {
@@ -27,7 +27,7 @@ pub struct MultiEntryMember {
/// The name of the member, used for display only.
pub display_name: String,
/// The actual name of the member, used to get the data from an Entry struct.
pub member_name: String
pub member_name: String,
}
impl MultiEntryMember {
@@ -35,7 +35,7 @@ impl MultiEntryMember {
pub fn new() -> Self {
Self {
display_name: String::new(),
member_name: String::new()
member_name: String::new(),
}
}
@@ -43,7 +43,7 @@ impl MultiEntryMember {
pub fn from(display_name: String, member_name: String) -> Self {
Self {
display_name,
member_name
member_name,
}
}
@@ -51,7 +51,7 @@ impl MultiEntryMember {
pub fn from_str(display_name: &str, member_name: &str) -> Self {
Self {
display_name: display_name.into(),
member_name: member_name.into()
member_name: member_name.into(),
}
}
}
@@ -85,9 +85,6 @@ pub fn Entry(
/// The secondary display member for this entry.
#[prop()]
info_member: Signal<MultiEntryMember>,
/// The child components of this Entry view.
#[prop()]
children: Children
) -> impl IntoView {
let nameData = entry_data.data.get(&name_member.get().member_name).unwrap();
let infoData = entry_data.data.get(&info_member.get().member_name).unwrap();
@@ -111,17 +108,19 @@ pub fn MultiEntry(
name_member: Signal<MultiEntryMember>,
/// The secondary display member for each entry object.
#[prop()]
info_member: Signal<MultiEntryMember>
info_member: Signal<MultiEntryMember>,
) -> impl IntoView {
// Creates an entry in the given entries list.
let create_entry = move |_| {
let mut new_entry = MultiEntryData::new();
new_entry
.data
.insert(name_member.get().member_name, ValueType::String(String::from("Testing")));
new_entry
.data
.insert(info_member.get().member_name, ValueType::String(new_entry.id.to_string()));
new_entry.data.insert(
name_member.get().member_name,
ValueType::String(String::from("Testing")),
);
new_entry.data.insert(
info_member.get().member_name,
ValueType::String(new_entry.id.to_string()),
);
entries.update(|entry_vec| entry_vec.push(new_entry));
};
+38
View File
@@ -0,0 +1,38 @@
use leptos::prelude::*;
/// A custom-styled select input.
///
/// Takes a `Vec<String>` as the set of inputs. Only one value may be selected, which is indicated
/// by the `value` prop of the component.
#[component]
pub fn Select(
#[prop(default = vec!["Test Value".into()])] value_list: Vec<String>,
#[prop(default = RwSignal::new("".into()))] value: RwSignal<String>,
#[prop(default = RwSignal::new("".into()))] label: RwSignal<String>,
#[prop(default = RwSignal::new(false))] disabled: RwSignal<bool>,
) -> impl IntoView {
view! {
<div>
<label>
{label}
<select
class="relative border-2 m-1.5 p-1.5 rounded-md"
class=(["border-red-700", "bg-transparent"], move || !disabled.get())
class=(["border-gray-600", "pointer-events-none", "bg-gray-600/33"], move || disabled.get())
on:change:target=move |e| {
value.set(e.target().value());
}>
// This closure handles the display of the options.
{move || {
value_list.iter().map(
move |value| {
let display = value.to_owned();
view! { <option value={display}>{display.clone()}</option> }
}
).collect::<Vec<_>>()
}}
</select>
</label>
</div>
}
}
+19 -6
View File
@@ -1,26 +1,39 @@
use std::str::FromStr;
use leptos::prelude::*;
#[component]
pub fn OutlinedTextField(
pub fn OutlinedTextField<T>(
#[prop(default = String::from(""))] placeholder: String,
#[prop(default = RwSignal::new("".to_string()))] value: RwSignal<String>,
#[prop(default = RwSignal::new(T::default()))] value: RwSignal<T>,
#[prop(default = RwSignal::new(false))] disabled: RwSignal<bool>,
#[prop(default = String::from(""))] name: String,
#[prop(default = String::from(""))] label: String,
#[prop(default = String::from(""))] input_type: String,
) -> impl IntoView {
) -> impl IntoView
where
T: Default + 'static + FromStr + ToString + PartialEq + Send + Sync + Clone
{
// Parsing logic for non-string types which happens on each input.
let on_input = move |e| {
let input_value = event_target_value(&e);
let parsed_value = T::from_str(&input_value)
.unwrap_or_default();
value.set(parsed_value);
};
view! (
<label>
{ label }
<input
class="border-2 m-1.5 p-1.5 rounded-md bg-transparent mt-6 relative transition-all duration-400"
class=(["border-red-700"], move || !disabled.get())
class=(["border-gray-600", "pointer-events-none"], move || disabled.get())
class=(["border-red-700", "bg-transparent"], move || !disabled.get())
class=(["border-gray-600", "pointer-events-none", "bg-gray-600/33"], move || disabled.get())
r#type={input_type}
disabled={disabled}
placeholder={placeholder}
prop:name=name
bind:value=value // This is a two-way binding - it is allowed to read *and* write.
prop:value={move || value.get().to_string()} // Sets the initial value.
on:input=on_input // Sets the value on each input.
/>
</label>
)
+3 -1
View File
@@ -1,7 +1,9 @@
use leptos::prelude::event_target_value;
pub mod app;
mod common;
mod components;
mod pages;
mod common;
#[cfg(feature = "hydrate")]
#[wasm_bindgen::prelude::wasm_bindgen]
+125 -68
View File
@@ -1,38 +1,54 @@
use crate::components::{ActionButton, OutlinedTextField};
use crate::common::StudentInfo;
use leptos::leptos_dom::logging::console_log;
use leptos::prelude::*;
use std::collections::HashMap;
#[cfg(feature = "ssr")]
use aws_sdk_dynamodb::{error::ProvideErrorMetadata, types::AttributeValue, Client};
use crate::app::Unauthenticated;
use crate::common::{StudentInfo, StudentInfoReactive, UserClaims};
use crate::components::{ActionButton, Loading, OutlinedTextField, Select};
use leptos::leptos_dom::logging::console_log;
use leptos::prelude::*;
use leptos_oidc::{Algorithm, AuthLoaded, AuthSignal, Authenticated};
#[server(GetSubmission, endpoint = "/get-submission")]
pub async fn get_submission(id: String) -> Result<StudentInfo, ServerFnError> {
let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
let dbclient = Client::new(&config);
console_log(format!("Getting values from API using key {}", id).as_str());
console_log(format!("Getting values from API using username {}", id).as_str());
match dbclient
.get_item()
.table_name("student-applications")
.key("Email", AttributeValue::S(id))
.key("Email", AttributeValue::S(id.clone()))
.send()
.await
{
Ok(output) => {
console_log("Found submission with passed key.");
let item = output.item.unwrap();
let first_name = item
let first_name: String = item
.get("studentFirstName")
.unwrap()
.as_s()
.unwrap()
.clone();
let last_name = item.get("studentLastName").unwrap().as_s().unwrap().clone();
.and_then(|attr| attr.as_s().ok())
.map(|s| s.to_owned())
.unwrap_or_default();
let last_name: String = item
.get("studentLastName")
.and_then(|attr| attr.as_s().ok())
.map(|s| s.to_owned())
.unwrap_or_default();
let math_sat_score = item
.get("mathSAT")
.and_then(|attr| attr.as_n().ok())
.map(|n| n.parse::<i32>().unwrap_or(0))
.unwrap_or_default();
console_log(format!("Got values from API: {} {}", first_name, last_name).as_str());
Ok(StudentInfo {
first_name,
last_name,
math_sat_score,
})
}
Err(err) => {
@@ -43,7 +59,10 @@ pub async fn get_submission(id: String) -> Result<StudentInfo, ServerFnError> {
}
#[server(CreateSampleSubmission, endpoint = "/create-sample-submission")]
pub async fn create_sample_submission(student_info: StudentInfo) -> Result<(), ServerFnError> {
pub async fn create_sample_submission(
student_info: StudentInfo,
subject: String,
) -> Result<(), ServerFnError> {
use aws_sdk_dynamodb::{types::AttributeValue, Client};
let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
@@ -51,18 +70,16 @@ pub async fn create_sample_submission(student_info: StudentInfo) -> Result<(), S
console_log(
format!(
"Creating sample submission with name {} {}",
student_info.first_name, student_info.last_name
"Creating sample submission with name {} {}, math score {}, and subject {}",
student_info.first_name, student_info.last_name, student_info.math_sat_score, subject
)
.as_str(),
);
let submission_id = String::from("testing_student");
match dbclient
.update_item()
.table_name("student-applications")
.key("Email", AttributeValue::S(submission_id))
.key("Email", AttributeValue::S(subject))
.expression_attribute_values(
":studentFirstName",
AttributeValue::S(student_info.first_name),
@@ -71,10 +88,17 @@ pub async fn create_sample_submission(student_info: StudentInfo) -> Result<(), S
":studentLastName",
AttributeValue::S(student_info.last_name),
)
.expression_attribute_values(
":mathScoreSAT",
AttributeValue::N(student_info.math_sat_score.to_string()),
)
.expression_attribute_names("#studentFirstName", "studentFirstName")
.expression_attribute_names("#studentLastName", "studentLastName")
.expression_attribute_names("#mathScoreSAT", "mathScoreSAT")
.update_expression(
"SET #studentFirstName = :studentFirstName, #studentLastName = :studentLastName",
concat!("SET #studentFirstName = :studentFirstName, ",
"#studentLastName = :studentLastName, ",
"#mathScoreSAT = :mathScoreSAT"),
)
.send()
.await
@@ -86,65 +110,98 @@ pub async fn create_sample_submission(student_info: StudentInfo) -> Result<(), S
#[component]
pub fn HomePage() -> impl IntoView {
let sample_id = RwSignal::new("google_113247439743075864879".to_string());
// Creates a reactive value to update the button
let count = RwSignal::new(0);
let elements_disabled = RwSignal::new(false);
let auth = use_context::<AuthSignal>().expect("Couldn't find user information.");
let user_claims = Signal::derive(move || {
auth.with(|auth| {
auth.authenticated().and_then(|data| {
data.decoded_access_token::<UserClaims>(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::new(
move || sample_id.get(),
async |id| {
get_submission(id).await.unwrap_or_else(|e| {
move || user_claims.get().map(|claim| claim.claims.subject.clone()),
async |opt_username| match opt_username {
Some(username) => get_submission(username).await.unwrap_or_else(|e| {
console_log(e.to_string().as_str());
StudentInfo {
first_name: String::from("Error"),
last_name: String::from(""),
math_sat_score: 0,
}
})
}),
None => StudentInfo {
first_name: String::new(),
last_name: String::new(),
math_sat_score: 0,
},
},
);
let on_click = move |_| {
*count.write() += 1;
*elements_disabled.write() = count.get() == 10;
};
let submit_action = ServerAction::<CreateSampleSubmission>::new();
view! {
<h1>"Welcome to Leptos!"</h1>
<p>
"Lillian's reported full name from the API: "
<Suspense fallback=move || view! { <span>"Loading..."</span> }>
{move || {
server_resource.get().map(|submission| {
view! { <span>{submission.first_name}" "{submission.last_name}</span> }
})
}}
</Suspense>
</p>
<ActionForm action=submit_action>
<div>
<OutlinedTextField
label="First Name".into()
placeholder="John".into()
disabled={elements_disabled}
name="student_info[first_name]".into() />
</div>
<div>
<OutlinedTextField
label="Last Name".into()
placeholder="Smith".into()
disabled={elements_disabled}
name="student_info[last_name]".into() />
</div>
<div>
<ActionButton
on:click=on_click
disabled={elements_disabled}
button_type="submit".into()
>
"Submit"
</ActionButton>
</div>
</ActionForm>
<AuthLoaded fallback=Loading>
<Authenticated unauthenticated=Unauthenticated>
// Replace this fallback with a real loading screen.
<Suspense fallback=Loading>
{move || {
server_resource.get().map(|submission: StudentInfo| {
let reactive_info = StudentInfoReactive::new(submission);
let select_value = RwSignal::new(String::from("Math"));
view! {
<p>
"Current user's reported full name from the API: "
{reactive_info.first_name}" "{reactive_info.last_name}
</p>
<form>
<div>
<OutlinedTextField
label="First Name".into()
placeholder="John".into()
value=reactive_info.first_name />
</div>
<div>
<OutlinedTextField
label="Last Name".into()
placeholder="Smith".into()
value=reactive_info.last_name />
</div>
<div>
<OutlinedTextField
label="Testing".into()
placeholder="Test".into()
value=reactive_info.math_sat_score />
</div>
<Select
value_list = vec!["Math".into(), "English".into(), "Science".into()]
value = select_value
/>
<div>
<ActionButton
on:click=move |_| {
submit_action.dispatch(CreateSampleSubmission {
student_info: reactive_info.capture(),
subject: user_claims.get().unwrap().claims.subject.clone()
});
}
>
"Submit"
</ActionButton>
</div>
</form>
}
})
}}
</Suspense>
</Authenticated>
</AuthLoaded>
}
}
+5 -8
View File
@@ -1,23 +1,20 @@
//! This defines the base component of the Chromebook loaner kiosk application.
//! This defines the base component of the Chromebook loaner kiosk application.
//! There are going to be 3 definitive screens that the users are able to see and use:
//! - The username entry screen: users will enter their name and email.
//! - The action selection screen: users will be given a choice regarding their loaner status.
//! - The completion screen: users will be given a success message should their information save.
//! Refer to each component's file for more information about how they work.
use leptos::prelude::*;
use super::{action_selection::ActionSelection, user_entry::UserEntry};
use crate::common::LoanerUser;
use super::{
user_entry::UserEntry,
action_selection::ActionSelection
};
use leptos::prelude::*;
/// Used to keep track of the currently displayed controls.
#[derive(Clone, Copy, PartialEq)]
pub enum LoanerKioskState {
UserEntry,
ActionSelection,
Complete
Complete,
}
/// The base Chromebook loaner kiosk component.
@@ -27,7 +24,7 @@ pub enum LoanerKioskState {
pub fn LoanerKiosk() -> impl IntoView {
let current_state = RwSignal::new(LoanerKioskState::UserEntry);
let user = RwSignal::new(LoanerUser::new());
view! {
<div class="flex w-screen h-screen justify-center items-center">
<UserEntry
+2 -3
View File
@@ -1,4 +1,3 @@
mod loaner_kiosk;
mod user_entry;
mod action_selection;
mod loaner_kiosk;
mod user_entry;
+12 -6
View File
@@ -1,4 +1,4 @@
//! Contains the user entry component.
//! Contains the user entry component.
//! It would be preferred if the user was restricted to a real user account. This is the URL:
//! https://admin.googleapis.com/admin/directory/v1/users?domain=region15.org&projection=BASIC&query=isArchived=false%20isAdmin=false
//! Once they select this, they can then check in/out the loaners under their account.
@@ -6,10 +6,10 @@
//! a service account. That's going to be a pain to set up.
//! For example's sake here, we're just going to take a first name, last name, and email address.
use leptos::leptos_dom::logging::console_log;
use leptos::prelude::*;
use crate::common::LoanerUser;
use crate::components::{ActionButton, OutlinedTextField};
use leptos::leptos_dom::logging::console_log;
use leptos::prelude::*;
/// The user entry component for the loaner kiosk.
///
@@ -20,11 +20,14 @@ use crate::components::{ActionButton, OutlinedTextField};
#[component]
pub fn UserEntry(
/// A callback function used when the control has determined this stage is finished.
#[prop()] mut on_submit: impl FnMut() + 'static,
#[prop()]
mut on_submit: impl FnMut() + 'static,
/// The global user record.
#[prop()] user: RwSignal<LoanerUser>,
#[prop()]
user: RwSignal<LoanerUser>,
/// Whether this control is shown. Required for use with CSS animations.
#[prop()] shown: bool
#[prop()]
shown: bool,
) -> impl IntoView {
let disabled = RwSignal::new(false);
let on_click = move |_| {
@@ -44,16 +47,19 @@ pub fn UserEntry(
label="First Name".into()
placeholder="John".into()
disabled={disabled}
value=RwSignal::new("".to_owned())
/>
<OutlinedTextField
label="Last Name".into()
placeholder="Smith".into()
disabled={disabled}
value=RwSignal::new("".to_owned())
/>
<OutlinedTextField
label="Region 15 Email".into()
placeholder="jsmith@region15.org".into()
disabled={disabled}
value=RwSignal::new("".to_owned())
/>
<ActionButton
on:click=on_click
+1 -1
View File
@@ -1,6 +1,6 @@
mod about_page;
mod home_page;
mod provider_entry;
mod loaner_kiosk;
mod provider_entry;
pub use self::{about_page::*, home_page::*, provider_entry::*};
+38 -19
View File
@@ -1,27 +1,46 @@
use crate::components::{MultiEntry, MultiEntryData, MultiEntryMember, OutlinedTextField};
use crate::app::Unauthenticated;
use crate::common::UserClaims;
use crate::components::{Loading, MultiEntry, MultiEntryData, MultiEntryMember, OutlinedTextField};
use leptos::leptos_dom::logging::console_log;
use leptos::prelude::*;
use leptos_oidc::{Algorithm, AuthLoaded, AuthSignal, Authenticated, LogoutLink};
use serde::{Deserialize, Serialize};
#[component]
pub fn ProviderEntry() -> impl IntoView {
let auth = use_context::<AuthSignal>().expect("Couldn't find auth signal");
let auth_data = auth
.get()
.authenticated()
.and_then(|data| data.decoded_access_token::<UserClaims>(Algorithm::RS256, &["account"]));
let multi_entries: RwSignal<Vec<MultiEntryData>> = RwSignal::new(vec![]);
console_log(format!("User claims: {auth_data:?}").as_str());
view! (
<h1>This is the provider entry page</h1>
<h2>It would usually have a series of questions that we ask the providers</h2>
<div>
<OutlinedTextField
placeholder = "Testing information...".into()
name = "testing_input".into()
label = "This is a testing question.".into()
/>
</div>
<div>
<p>This is an example of the multientry element.</p>
<MultiEntry
entries=multi_entries
name_member=Signal::from(MultiEntryMember::from_str("Entry Name", "name"))
info_member=Signal::from(MultiEntryMember::from_str("Entry Info", "info"))
/>
</div>
<AuthLoaded fallback=Loading>
<Authenticated unauthenticated=Unauthenticated>
<LogoutLink class="text-logout">"Sign out"</LogoutLink>
<h1>This is the provider entry page</h1>
<h2>It would usually have a series of questions that we ask the providers</h2>
<div>
<OutlinedTextField
placeholder = "Testing information...".into()
name = "testing_input".into()
label = "This is a testing question.".into()
value = RwSignal::new("".to_owned())
/>
</div>
<div>
<p>This is an example of the multientry element.</p>
<MultiEntry
entries=multi_entries
name_member=Signal::from(MultiEntryMember::from_str("Entry Name", "name"))
info_member=Signal::from(MultiEntryMember::from_str("Entry Info", "info"))
/>
</div>
</Authenticated>
</AuthLoaded>
)
}