feat: add validation in checkbox list
This commit is contained in:
1 parent
94c2930d2a
commit
f863b4db18
3 files changed
+266
-204
No files matched your search
+122
-63
@@ -2,16 +2,17 @@ use crate::common::ValueType;
|
|||||||
use crate::components::utils::create_unique_id;
|
use crate::components::utils::create_unique_id;
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use crate::components::{FormValidationRegistry, InputState, ValidationState};
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Checkbox(
|
pub fn Checkbox(
|
||||||
#[prop()] checked: Signal<bool>,
|
#[prop()] checked: Signal<bool>,
|
||||||
#[prop(into)] on_change: Callback<(), ()>,
|
#[prop(into)] on_change: Callback<(), ()>,
|
||||||
#[prop(into)] value: String,
|
#[prop(into)] value: Signal<String>,
|
||||||
#[prop(into)] name: String,
|
#[prop(into)] name: String,
|
||||||
#[prop(into)] disabled: Signal<bool>,
|
#[prop(into)] disabled: Signal<bool>,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
let id = create_unique_id(&name, &value);
|
let id = create_unique_id(&name, &value.get_untracked());
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<label for=id class="flex items-center">
|
<label for=id class="flex items-center">
|
||||||
@@ -42,11 +43,23 @@ pub fn Checkbox(
|
|||||||
rotate-45 transform border-b-3 border-r-3
|
rotate-45 transform border-b-3 border-r-3
|
||||||
border-white transition-opacity duration-150"></span>
|
border-white transition-opacity duration-150"></span>
|
||||||
</div>
|
</div>
|
||||||
<span>{value.clone()}</span>
|
<span>{value}</span>
|
||||||
</label>
|
</label>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn value_type_to_vec(value_type: &ValueType) -> Vec<String> {
|
||||||
|
let ValueType::List(Some(list)) = value_type else {
|
||||||
|
return Vec::new();
|
||||||
|
};
|
||||||
|
|
||||||
|
list.iter().map(|val| {
|
||||||
|
val.as_string()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.unwrap_or_default()
|
||||||
|
}).collect::<Vec<String>>()
|
||||||
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn CheckboxList(
|
pub fn CheckboxList(
|
||||||
#[prop(optional)] selected: RwSignal<Vec<String>>,
|
#[prop(optional)] selected: RwSignal<Vec<String>>,
|
||||||
@@ -55,71 +68,117 @@ pub fn CheckboxList(
|
|||||||
#[prop()] items: Vec<String>,
|
#[prop()] items: Vec<String>,
|
||||||
#[prop(optional, into)] disabled: Signal<bool>,
|
#[prop(optional, into)] disabled: Signal<bool>,
|
||||||
#[prop(optional, into)] label: String,
|
#[prop(optional, into)] label: String,
|
||||||
|
#[prop(optional, into)] required: Signal<bool>
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
|
//#region Central List Logic
|
||||||
|
|
||||||
|
// Get default values from the data map. This will only run once, when the input is created
|
||||||
|
// and mounted to the DOM.
|
||||||
|
let temp_map = data_map.get_untracked();
|
||||||
|
let origin_value_type = temp_map.get(&data_member)
|
||||||
|
.unwrap_or(&ValueType::List(None));
|
||||||
|
let origin_list = value_type_to_vec(origin_value_type);
|
||||||
|
|
||||||
|
// "Raw" selected values - just strings, no ValueTyping
|
||||||
|
let selected_list = RwSignal::new(origin_list);
|
||||||
|
|
||||||
|
Effect::new({
|
||||||
|
let data_member = data_member.clone();
|
||||||
|
move || {
|
||||||
|
// Convert selected strings into ValueTypes
|
||||||
|
let typed_list = selected_list.with(|list|
|
||||||
|
list.iter().cloned().map(|val|
|
||||||
|
ValueType::String(Some(val))
|
||||||
|
).collect::<Vec<ValueType>>()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update the map
|
||||||
|
data_map.update(|map| {
|
||||||
|
map.insert(data_member.clone(), ValueType::List(Some(typed_list)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
//#region Form Validation
|
||||||
|
|
||||||
|
let validation_context = use_context::<FormValidationRegistry>()
|
||||||
|
.expect("FormValidationRegistry was not found");
|
||||||
|
|
||||||
|
let error = Signal::derive(move || {
|
||||||
|
if required.get() && selected_list.get().len() <= 0 {
|
||||||
|
ValidationState::Invalid("This field is required.".to_string())
|
||||||
|
} else {
|
||||||
|
ValidationState::Valid
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let dirty = RwSignal::new(false);
|
||||||
|
let show_errors = Signal::derive(move || dirty.get() && matches!(error.get(), ValidationState::Invalid(_)));
|
||||||
|
|
||||||
|
let validator = RwSignal::new(InputState::new(
|
||||||
|
data_member.clone(),
|
||||||
|
error.clone(),
|
||||||
|
dirty.clone()
|
||||||
|
));
|
||||||
|
|
||||||
|
validation_context.validators.update(|list| list.push(validator.clone()));
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
//#region Render Logic
|
||||||
view! {
|
view! {
|
||||||
<div class="m-1.5 mt-0 mb-0">
|
<div class="flex flex-col">
|
||||||
<span class="font-bold">{label}</span>
|
<div class="m-1.5 mt-0 mb-0">
|
||||||
{items
|
<span class="font-bold">{label}</span>
|
||||||
.into_iter()
|
{items
|
||||||
.map(|item| {
|
.into_iter()
|
||||||
let checked_signal = Signal::derive({
|
.map(|item| {
|
||||||
let item_name = item.clone();
|
let item = Signal::derive({
|
||||||
let data_member = data_member.clone();
|
let cloned = item.clone();
|
||||||
move || {
|
move || cloned.clone()
|
||||||
data_map.get() // HashMap
|
});
|
||||||
.get(&data_member) // Option<ValueType>
|
|
||||||
.unwrap_or(&ValueType::List(None)) // ValueType
|
|
||||||
.as_list() // Result<Option<Vec<ValueType>>, ValueType>
|
|
||||||
.unwrap_or(Some(vec!())) // Option<Vec<ValueType>>
|
|
||||||
.unwrap_or(vec!()) // Vec<ValueType>
|
|
||||||
.contains(&ValueType::String(Some(item_name.clone()))) // bool
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let on_change = {
|
let checked = Signal::derive(move || {
|
||||||
let item_name = item.clone();
|
selected_list.get().contains(&item.get())
|
||||||
let data_member = data_member.clone();
|
});
|
||||||
move || {
|
|
||||||
let result = ValueType::String(Some(item_name.clone()));
|
let on_change = move || {
|
||||||
data_map.update(|map| {
|
// Update the data list by checking if the value is present in it. If so,
|
||||||
// Attempt to get the value type from the hash map.
|
// remove it. If not, add it.
|
||||||
match map.get_mut(&data_member) {
|
selected_list.update(|list| {
|
||||||
Some(value_type) => {
|
let item = item.get();
|
||||||
// Attempt to get the value as a list.
|
if list.contains(&item) {
|
||||||
match value_type.as_list().unwrap_or(None) {
|
list.retain(|val| *val != item);
|
||||||
Some(mut list) => {
|
} else {
|
||||||
// Attempt to find the selected value in the selected list.
|
list.push(item);
|
||||||
// If it's present, remove it, or vice versa.
|
}
|
||||||
match list.iter().position(|val| *val == result) {
|
|
||||||
Some(index) => { list.remove(index); },
|
|
||||||
None => { list.push(result); }
|
|
||||||
};
|
|
||||||
// Update the existing value_type entry in the hash map.
|
|
||||||
*value_type = ValueType::List(Some(list));
|
|
||||||
},
|
|
||||||
// Insert a new value_type entry.
|
|
||||||
None => { *value_type = ValueType::List(Some(vec![result])); }
|
|
||||||
};
|
|
||||||
},
|
|
||||||
// Insert a new value_type entry.
|
|
||||||
None => { map.insert(data_member.clone(), ValueType::List(Some(vec![result]))); }
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
}
|
dirty.set(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked=checked_signal
|
checked=checked
|
||||||
on_change=on_change
|
on_change=on_change
|
||||||
// The actual selected values are tracked by this element, not by the checkboxes themselves.
|
// The actual selected values are tracked by this element, not by the checkboxes themselves.
|
||||||
value=item.clone()
|
value=item
|
||||||
name=data_member.clone()
|
name=data_member.clone()
|
||||||
disabled=disabled
|
disabled=disabled
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()}
|
.collect_view()}
|
||||||
|
</div>
|
||||||
|
<Show when=move || show_errors.get()>
|
||||||
|
<div class="text-red-600 text-sm mr-1.5 ml-1.5">
|
||||||
|
{move || {
|
||||||
|
match error.get() {
|
||||||
|
ValidationState::Invalid(msg) => msg,
|
||||||
|
_ => "There is no error - should not see this message.".to_string()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
//#endregion
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
// Server dependencies
|
// Server dependencies
|
||||||
#[cfg(feature = "ssr")]
|
#[cfg(feature = "ssr")]
|
||||||
use aws_sdk_dynamodb::{Client, error::ProvideErrorMetadata, types::AttributeValue};
|
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};
|
||||||
#[cfg(feature = "ssr")]
|
#[cfg(feature = "ssr")]
|
||||||
@@ -310,6 +309,7 @@ pub fn HomePage() -> impl IntoView {
|
|||||||
disabled=elements_disabled
|
disabled=elements_disabled
|
||||||
data_member="favorite_candies"
|
data_member="favorite_candies"
|
||||||
data_map=form_data
|
data_map=form_data
|
||||||
|
required=true
|
||||||
/>
|
/>
|
||||||
</Row>
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
|
|||||||
+143
-140
@@ -1,3 +1,4 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
use leptos::either::Either;
|
use leptos::either::Either;
|
||||||
use leptos::html::Dialog;
|
use leptos::html::Dialog;
|
||||||
use leptos::logging::log;
|
use leptos::logging::log;
|
||||||
@@ -5,9 +6,8 @@ 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};
|
use crate::components::{ActionButton, Banner, ChipsList, Loading, OutlinedTextField, Panel, RadioList, Row, TextFieldType, ValidatedForm};
|
||||||
use super::UnauthenticatedPage;
|
use super::UnauthenticatedPage;
|
||||||
use traits::{AsReactive, ReactiveCapture};
|
|
||||||
use crate::common::ComparisonData;
|
use crate::common::ComparisonData;
|
||||||
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, CreateTestComparisons, RegisterScholarship, DeleteProviderScholarship};
|
use super::api::{get_comparison_info, get_provider_scholarships, get_scholarship_info, CreateScholarshipInfo, CreateTestComparisons, RegisterScholarship, DeleteProviderScholarship};
|
||||||
@@ -357,14 +357,14 @@ fn ScholarshipForm(
|
|||||||
_ => false
|
_ => false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let result_msg = Signal::derive(move || {
|
// let result_msg = Signal::derive(move || {
|
||||||
match submit_status.get() {
|
// match submit_status.get() {
|
||||||
SubmitStatus::Idle => "".into(),
|
// SubmitStatus::Idle => "".into(),
|
||||||
SubmitStatus::Sending => "Sending submission...".into(),
|
// SubmitStatus::Sending => "Sending submission...".into(),
|
||||||
SubmitStatus::Success => "Success".into(),
|
// SubmitStatus::Success => "Success".into(),
|
||||||
SubmitStatus::Error(msg) => format!("Error: {}", msg),
|
// SubmitStatus::Error(msg) => format!("Error: {}", msg),
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
let submit_action = ServerAction::<CreateScholarshipInfo>::new();
|
let submit_action = ServerAction::<CreateScholarshipInfo>::new();
|
||||||
|
|
||||||
@@ -407,6 +407,14 @@ fn ScholarshipForm(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let form_data = RwSignal::new(HashMap::new());
|
||||||
|
|
||||||
|
Effect::new(move || {
|
||||||
|
if let Some(Ok(scholarship)) = scholarship_info.get() {
|
||||||
|
form_data.set(scholarship.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let comparison_ids = Memo::new(move |_| {
|
let comparison_ids = Memo::new(move |_| {
|
||||||
comparison_list.get().map(|list| list.iter()
|
comparison_list.get().map(|list| list.iter()
|
||||||
.map(|comp| comp.clone().id)
|
.map(|comp| comp.clone().id)
|
||||||
@@ -425,6 +433,16 @@ 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 on_submit = move |_| {
|
||||||
|
let mut info = ExpandableInfo::new(scholarship_id.get().unwrap_or_default());
|
||||||
|
info.data = form_data.get();
|
||||||
|
|
||||||
|
log!("Map values: {:?}", info.data);
|
||||||
|
submit_action.dispatch(CreateScholarshipInfo {
|
||||||
|
info
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<Panel>
|
<Panel>
|
||||||
<Show
|
<Show
|
||||||
@@ -432,137 +450,122 @@ fn ScholarshipForm(
|
|||||||
fallback=|| view! { <p>"Choose a scholarship from the right, or create a new one."</p> }
|
fallback=|| view! { <p>"Choose a scholarship from the right, or create a new one."</p> }
|
||||||
>
|
>
|
||||||
<Suspense fallback=Loading>
|
<Suspense fallback=Loading>
|
||||||
{move || {
|
<ValidatedForm on_submit=Callback::new(on_submit)>
|
||||||
scholarship_info.get()
|
{move || {
|
||||||
.map(|res_scholarship| {
|
scholarship_info.get()
|
||||||
match res_scholarship {
|
.map(|res_scholarship| {
|
||||||
Ok(scholarship) => {
|
match res_scholarship {
|
||||||
let reactive_info = scholarship.as_reactive();
|
Ok(scholarship) => {
|
||||||
|
Either::Left(view! {
|
||||||
Either::Left(view! {
|
<Row>
|
||||||
<Row>
|
<h1 class="text-3xl font-bold">
|
||||||
<h1 class="text-3xl font-bold">
|
"Scholarship Info Form (test)"
|
||||||
"Scholarship Info Form (test)"
|
</h1>
|
||||||
</h1>
|
</Row>
|
||||||
</Row>
|
<Row>
|
||||||
<Row>
|
<OutlinedTextField
|
||||||
<OutlinedTextField
|
label="Scholarship Name"
|
||||||
label="Scholarship Name"
|
placeholder="Example Scholarship Name"
|
||||||
placeholder="Example Scholarship Name"
|
disabled=elements_disabled
|
||||||
disabled=elements_disabled
|
data_member="name"
|
||||||
data_member="name"
|
data_map=form_data
|
||||||
data_map=reactive_info.data
|
/>
|
||||||
/>
|
</Row>
|
||||||
</Row>
|
<Row>
|
||||||
<Row>
|
<OutlinedTextField
|
||||||
<OutlinedTextField
|
label="Amount per award: (if multiple, enter the highest value)"
|
||||||
label="Amount per award: (if multiple, enter the highest value)"
|
placeholder="Enter an amount..."
|
||||||
placeholder="Enter an amount..."
|
disabled=elements_disabled
|
||||||
disabled=elements_disabled
|
data_member="amount_per_award"
|
||||||
data_member="amount_per_award"
|
data_map=form_data
|
||||||
data_map=reactive_info.data
|
input_type=TextFieldType::Number
|
||||||
input_type=TextFieldType::Number
|
/>
|
||||||
/>
|
</Row>
|
||||||
</Row>
|
<Row>
|
||||||
<Row>
|
<OutlinedTextField
|
||||||
<OutlinedTextField
|
label="Total number of awards:"
|
||||||
label="Total number of awards:"
|
placeholder="Enter an amount..."
|
||||||
placeholder="Enter an amount..."
|
disabled=elements_disabled
|
||||||
disabled=elements_disabled
|
data_member="num_awards"
|
||||||
data_member="num_awards"
|
data_map=form_data
|
||||||
data_map=reactive_info.data
|
input_type=TextFieldType::Number
|
||||||
input_type=TextFieldType::Number
|
/>
|
||||||
/>
|
</Row>
|
||||||
</Row>
|
<Row>
|
||||||
<Row>
|
<OutlinedTextField
|
||||||
<OutlinedTextField
|
label="Total amount of all awards:"
|
||||||
label="Total amount of all awards:"
|
placeholder="Enter an amount..."
|
||||||
placeholder="Enter an amount..."
|
disabled=elements_disabled
|
||||||
disabled=elements_disabled
|
data_member="total_awards"
|
||||||
data_member="total_awards"
|
data_map=form_data
|
||||||
data_map=reactive_info.data
|
input_type=TextFieldType::Number
|
||||||
input_type=TextFieldType::Number
|
/>
|
||||||
/>
|
</Row>
|
||||||
</Row>
|
<Row>
|
||||||
<Row>
|
<RadioList
|
||||||
<RadioList
|
data_member="fafsa_required"
|
||||||
data_member="fafsa_required"
|
data_map=form_data
|
||||||
data_map=reactive_info.data
|
items=vec!["Yes".to_string(), "No".to_string()]
|
||||||
items=vec!["Yes".to_string(), "No".to_string()]
|
disabled=elements_disabled
|
||||||
disabled=elements_disabled
|
label="Do you require student financial information?"
|
||||||
label="Do you require student financial information?"
|
/>
|
||||||
/>
|
</Row>
|
||||||
</Row>
|
<Row>
|
||||||
<Row>
|
<RadioList
|
||||||
<RadioList
|
data_member="transcript_required"
|
||||||
data_member="transcript_required"
|
data_map=form_data
|
||||||
data_map=reactive_info.data
|
items=vec!["Yes".to_string(), "No".to_string()]
|
||||||
items=vec!["Yes".to_string(), "No".to_string()]
|
disabled=elements_disabled
|
||||||
disabled=elements_disabled
|
label="Do you require a student transcript?"
|
||||||
label="Do you require a student transcript?"
|
/>
|
||||||
/>
|
</Row>
|
||||||
</Row>
|
<Row>
|
||||||
<Row>
|
<RadioList
|
||||||
<RadioList
|
data_member="award_to"
|
||||||
data_member="award_to"
|
data_map=form_data
|
||||||
data_map=reactive_info.data
|
items=vec!["School".to_string(), "Student".to_string()]
|
||||||
items=vec!["School".to_string(), "Student".to_string()]
|
disabled=elements_disabled
|
||||||
disabled=elements_disabled
|
label="Will the award be made to the school or the student?"
|
||||||
label="Will the award be made to the school or the student?"
|
/>
|
||||||
/>
|
</Row>
|
||||||
</Row>
|
<Row>
|
||||||
<Row>
|
<RadioList
|
||||||
<RadioList
|
data_member="essay_required"
|
||||||
data_member="essay_required"
|
data_map=form_data
|
||||||
data_map=reactive_info.data
|
items=vec!["Yes".to_string(), "No".to_string()]
|
||||||
items=vec!["Yes".to_string(), "No".to_string()]
|
disabled=elements_disabled
|
||||||
disabled=elements_disabled
|
label="Do you require a student essay?"
|
||||||
label="Do you require a student essay?"
|
/>
|
||||||
/>
|
</Row>
|
||||||
</Row>
|
<Row>
|
||||||
<Row>
|
<RadioList
|
||||||
<RadioList
|
data_member="essay_prompt"
|
||||||
data_member="essay_prompt"
|
data_map=form_data
|
||||||
data_map=reactive_info.data
|
items=vec!["Test 1", "Test 2", "Test 3"].iter().map(|s| s.to_string()).collect()
|
||||||
items=vec!["Test 1", "Test 2", "Test 3"].iter().map(|s| s.to_string()).collect()
|
disabled=elements_disabled
|
||||||
disabled=elements_disabled
|
label="If so, select a prompt from the list below."
|
||||||
label="If so, select a prompt from the list below."
|
/>
|
||||||
/>
|
</Row>
|
||||||
</Row>
|
<Row>
|
||||||
<Row>
|
<ChipsList
|
||||||
<ChipsList
|
label="Scholarship Requirements"
|
||||||
label="Scholarship Requirements"
|
data_member="requirements"
|
||||||
data_member="requirements"
|
data_map=form_data
|
||||||
data_map=reactive_info.data
|
values=comparison_ids
|
||||||
values=comparison_ids
|
displayed_text=comparison_text
|
||||||
displayed_text=comparison_text
|
/>
|
||||||
/>
|
</Row>
|
||||||
</Row>
|
})
|
||||||
<Row>
|
}
|
||||||
<ActionButton
|
Err(err) => {
|
||||||
disabled=elements_disabled
|
Either::Right(view! {
|
||||||
on:click=move |_| {
|
<p>"Error while getting scholarship: "{err.to_string()}</p>
|
||||||
let captured = reactive_info.capture();
|
})
|
||||||
log!("Map values: {:?}", captured);
|
}
|
||||||
submit_action.dispatch(CreateScholarshipInfo {
|
|
||||||
info: captured.clone()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
>"Submit"</ActionButton>
|
|
||||||
</Row>
|
|
||||||
<Row>
|
|
||||||
<p>{result_msg}</p>
|
|
||||||
</Row>
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
Err(err) => {
|
}).collect_view()
|
||||||
Either::Right(view! {
|
}}
|
||||||
<p>"Error while getting scholarship: "{err.to_string()}</p>
|
</ValidatedForm>
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).collect_view()
|
|
||||||
}}
|
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</Show>
|
</Show>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|||||||
Reference in new issue
Block a user