A massive, unorganized PR. #44

Merged
BobdaFett merged 15 commits from feature/categorize-chips into main 2026-01-13 09:50:27 +00:00
4 changed files with 72 additions and 65 deletions
Showing only changes of commit 0742e833da - Show all commits

No files matched your search

+18 -17
View File
@@ -57,8 +57,8 @@ pub fn OutlinedTextField(
#[prop(optional, into)] placeholder: String, #[prop(optional, into)] placeholder: String,
#[prop(into)] data_member: String, // should this be an RwSignal?? #[prop(into)] data_member: String, // should this be an RwSignal??
#[prop()] data_map: RwSignal<HashMap<String, ValueType>>, #[prop()] data_map: RwSignal<HashMap<String, ValueType>>,
#[prop(optional)] input_type: TextFieldType, #[prop(optional, into)] input_type: Signal<TextFieldType>,
#[prop(into, optional)] disabled: Signal<bool>, #[prop(optional, into)] disabled: Signal<bool>,
#[prop(optional, into)] name: String, #[prop(optional, into)] name: String,
#[prop(optional, into)] label: String, #[prop(optional, into)] label: String,
#[prop(optional, into)] required: Signal<bool> #[prop(optional, into)] required: Signal<bool>
@@ -69,13 +69,20 @@ pub fn OutlinedTextField(
let validator_context = use_validation_context() let validator_context = use_validation_context()
.expect("FormValidSignal was not found"); .expect("FormValidSignal was not found");
let raw_value = RwSignal::new(data_map.get_untracked() let raw_value = Signal::derive({
.get(&data_member) let data_member = data_member.clone();
.unwrap_or(&ValueType::String(None)) move ||
.to_string()); data_map
.get()
.get(&data_member)
.unwrap_or(&ValueType::String(None))
.to_string()
});
let dirty = RwSignal::new(false); let dirty = RwSignal::new(false);
let error = RwSignal::new(validate(required.get_untracked(), &raw_value.get_untracked(), &input_type)); let error = Signal::derive(move ||
validate(required.get(), &raw_value.get(), &input_type.get())
);
let show_errors = Signal::derive(move || { let show_errors = Signal::derive(move || {
dirty.get() && matches!(error.get(), ValidationState::Invalid(_)) dirty.get() && matches!(error.get(), ValidationState::Invalid(_))
}); });
@@ -90,10 +97,10 @@ pub fn OutlinedTextField(
}); });
}); });
let str_type = match input_type { let str_type = Signal::derive(move || match input_type.get_untracked() {
TextFieldType::Number => "number", TextFieldType::Number => "number",
_ => "text" // This is fine even for emails, as we're doing our own validation. _ => "text" // This is fine even for emails, as we're doing our own validation.
}; });
let on_blur = move |_| { let on_blur = move |_| {
// Check validity of input's current value. The value will be updated by on_input, // Check validity of input's current value. The value will be updated by on_input,
@@ -107,9 +114,7 @@ pub fn OutlinedTextField(
let data_member = data_member.clone(); let data_member = data_member.clone();
move |e| { move |e| {
let to_parse = event_target_value(&e); let to_parse = event_target_value(&e);
raw_value.set(to_parse.clone()); let into_map = match input_type.get() {
error.set(validate(required.get(), &to_parse, &input_type));
let into_map = match input_type {
TextFieldType::Number => ValueType::Number(Some(to_parse)), TextFieldType::Number => ValueType::Number(Some(to_parse)),
_ => ValueType::String(Some(to_parse)) _ => ValueType::String(Some(to_parse))
}; };
@@ -137,11 +142,7 @@ pub fn OutlinedTextField(
disabled={disabled} disabled={disabled}
placeholder={placeholder} placeholder={placeholder}
prop:name=name prop:name=name
prop:value=move || data_map prop:value=raw_value
.get() // HashMap
.get(&data_member) // Option<ValueType>
.unwrap_or(&ValueType::String(None)) // ValueType
.to_string()
on:input=on_input on:input=on_input
on:blur=on_blur on:blur=on_blur
/> />
+14 -6
View File
@@ -1,7 +1,7 @@
use std::time::Duration; use std::time::Duration;
use leptos::prelude::*; use leptos::prelude::*;
use leptos_animate::animate; use leptos_animate::animate;
use leptos_animate::animations::classes::In; use leptos_animate::animations::classes::{In, Out};
#[component] #[component]
pub fn Toast( pub fn Toast(
@@ -17,11 +17,19 @@ pub fn Toast(
); );
view! { view! {
<div class="flex flex-col rounded-md shadow-md" <div
use:animate=In::default() class="flex flex-col rounded-md shadow-lg/33 bg-white p-3 m-1.5 mx-auto min-w-lg max-w-lg"
.source("opacity-0 -translate-y-2 -scale-10") use:animate=(
.active("transition-all duration-150") In::default()
.target("opacity-100")> .source("opacity-0 -translate-y-2 scale-90")
.active("transition-all duration-150 transform-gpu")
.target("opacity-100 translate-y-0 scale-100"),
Out::default()
.source("opacity-100 translate-y-0 scale-100")
.active("transition-all duration-150 transform-gpu")
.target("opacity-0 -translate-y-2 scale-90"),
)
>
<h3 class="font-bold text-lg">{title}</h3> <h3 class="font-bold text-lg">{title}</h3>
<p>{message}</p> <p>{message}</p>
</div> </div>
+1 -1
View File
@@ -57,7 +57,7 @@ pub fn ToastList(
view! { view! {
{children()} {children()}
<div class="fixed top-1 left-1/2 right-1/2 -translate-x-1/2 min-w-lg"> <div class="fixed top-1 left-1/2 right-1/2 -translate-x-1/2 w-full">
<For <For
each=move || context.messages.get() each=move || context.messages.get()
key=|toast| toast.id.clone() key=|toast| toast.id.clone()
+39 -41
View File
@@ -6,10 +6,10 @@ use leptos::prelude::*;
use leptos_oidc::{AuthLoaded, Authenticated}; use leptos_oidc::{AuthLoaded, Authenticated};
use leptos_router::hooks::{use_navigate, use_params}; use leptos_router::hooks::{use_navigate, use_params};
use crate::common::{ExpandableInfo, ScholarshipFormParams, SubmitStatus, ValueType}; use crate::common::{ExpandableInfo, ScholarshipFormParams, SubmitStatus, ValueType};
use crate::components::{ActionButton, Banner, ChipsList, Loading, OutlinedTextField, Panel, RadioList, Row, TextFieldType, ValidatedForm}; use crate::components::{ActionButton, Banner, ChipsList, Loading, OutlinedTextField, Panel, RadioList, Row, TextFieldType, Toast, ToastContext, ToastList, ValidatedForm};
use super::UnauthenticatedPage; use super::UnauthenticatedPage;
use crate::pages::utils::get_user_claims; use crate::pages::utils::get_user_claims;
use super::api::{get_comparison_info, get_provider_scholarships, get_scholarship_info, CreateScholarshipInfo, RegisterScholarship, DeleteProviderScholarship, get_comparisons_categorized, CreateTestComparisons}; use super::api::{get_provider_scholarships, get_scholarship_info, CreateScholarshipInfo, RegisterScholarship, DeleteProviderScholarship, get_comparisons_categorized, CreateTestComparisons};
/// # Scholarship Info Page /// # Scholarship Info Page
@@ -45,26 +45,28 @@ pub fn ScholarshipInfoPage() -> impl IntoView {
logo="/PHS_Stacked_Acronym.png" logo="/PHS_Stacked_Acronym.png"
path="/" path="/"
/> />
<AuthLoaded fallback=Loading> <ToastList>
<Authenticated unauthenticated=UnauthenticatedPage> <AuthLoaded fallback=Loading>
<div class="flex flex-row align-top items-start"> <Authenticated unauthenticated=UnauthenticatedPage>
<div class="flex flex-col flex-1" /> <div class="flex flex-row align-top items-start">
<ScholarshipList <div class="flex flex-col flex-1" />
refresh_token=list_refresh <ScholarshipList
on_delete=on_delete refresh_token=list_refresh
/> on_delete=on_delete
<Transition fallback=Loading>
<ScholarshipForm
scholarship_id=scholarship_id
on_submit=move || {
list_refresh.update(|n| *n += 1);
}
/> />
</Transition> <Transition fallback=Loading>
<div class="flex flex-col flex-1" /> <ScholarshipForm
</div> scholarship_id=scholarship_id
</Authenticated> on_submit=move || {
</AuthLoaded> list_refresh.update(|n| *n += 1);
}
/>
</Transition>
<div class="flex flex-col flex-1" />
</div>
</Authenticated>
</AuthLoaded>
</ToastList>
} }
} }
@@ -349,15 +351,6 @@ fn ScholarshipForm(
/// submission fails, this function is not run. /// submission fails, this function is not run.
#[prop(into)] on_submit: Callback<()> #[prop(into)] on_submit: Callback<()>
) -> impl IntoView { ) -> impl IntoView {
// let result_msg = Signal::derive(move || {
// match submit_status.get() {
// SubmitStatus::Idle => "".into(),
// SubmitStatus::Sending => "Sending submission...".into(),
// SubmitStatus::Success => "Success".into(),
// SubmitStatus::Error(msg) => format!("Error: {}", msg),
// }
// });
let submit_action = ServerAction::<CreateScholarshipInfo>::new(); let submit_action = ServerAction::<CreateScholarshipInfo>::new();
let submit_status = RwSignal::new(SubmitStatus::Idle); let submit_status = RwSignal::new(SubmitStatus::Idle);
@@ -382,17 +375,6 @@ fn ScholarshipForm(
} }
}); });
let comparison_list = Resource::new(
move || scholarship_id.get(), // There's no input to this function.
async move |_| {
get_comparison_info().await
.unwrap_or_else(|e| {
log!("Failed to get comparison info: {:?}", e);
Vec::new()
})
}
);
let comparison_lists = Resource::new( let comparison_lists = Resource::new(
move || scholarship_id.get().is_some(), move || scholarship_id.get().is_some(),
async move |_| { async move |_| {
@@ -476,6 +458,8 @@ fn ScholarshipForm(
// fafsa_required, award_to, transcript_required, recipient_selection, essay_requirement (and prompt) // fafsa_required, award_to, transcript_required, recipient_selection, essay_requirement (and prompt)
// award_night_remarks // award_night_remarks
let mut toasts = expect_context::<ToastContext>();
let on_submit = move |_| { let on_submit = move |_| {
let mut info = ExpandableInfo::new(scholarship_id.get().unwrap_or_default()); let mut info = ExpandableInfo::new(scholarship_id.get().unwrap_or_default());
info.data = form_data.get(); info.data = form_data.get();
@@ -486,6 +470,20 @@ fn ScholarshipForm(
}); });
}; };
Effect::new(move || {
submit_action.value().get().is_some()
.then(|| {
log!("Displaying toasts!");
toasts.toast(
Toast::new()
.id(uuid::Uuid::new_v4())
.msg("Succesfully submitted.")
);
submit_action.clear();
});
});
let create_comps = ServerAction::<CreateTestComparisons>::new(); let create_comps = ServerAction::<CreateTestComparisons>::new();
view! { view! {