use leptos::either::Either;
use leptos::logging::log;
use leptos::prelude::*;
use leptos::reactive::spawn_local;
use leptos_oidc::{AuthLoaded, Authenticated};
use leptos_router::hooks::{use_navigate, use_params};
use crate::common::{ExpandableInfo, ScholarshipFormParams, SubmitStatus, ValueType};
use crate::components::{ActionButton, Banner, ChipsList, Loading, OutlinedTextField, Panel, RadioList, Row};
use super::UnauthenticatedPage;
use traits::{AsReactive, ReactiveCapture};
use crate::common::ComparisonData;
use crate::pages::utils::get_user_claims;
use super::api::{get_comparison_info, get_provider_scholarships, get_scholarship_info, delete_provider_scholarship, CreateScholarshipInfo, CreateTestComparisons, RegisterScholarship};
/// # Scholarship Info Page
/// This page will handle creating or editing scholarship information given a specific
/// scholarship ID and scholarship provider subject number. The form itself will only
/// track a single scholarship (for now) and so selection of what scholarship will be edited
/// must be handled *before* navigation to this page.
#[component]
pub fn ScholarshipInfoPage() -> impl IntoView {
let url_params = use_params::();
let scholarship_id = Memo::new(move |_| {
url_params.read()
.as_ref()
.ok()
.and_then(|params| params.id.clone())
});
let create_test_comps = ServerAction::::new();
// Remember that we only want to show the page after we've got authentication information
// and if there's data for us to use.
view! {
"Create test comparisons"
}
}
/// # Scholarship List Component
///
/// This component should always be wrapped in an Authenticated component. Assuming that a provider
/// is authenticated, this component will get all the scholarships that this provider owns and
/// will display them in a list, giving them the option to edit, delete, or create new scholarships.
///
/// It does not accept any props, and instead pulls the provider's information from the surrounding
/// environment.
///
/// Example usage:
/// ```
/// view! {
///
///
///
///
///
/// }
/// ```
#[component]
fn ScholarshipList() -> impl IntoView {
// Get scholarship provider information from authentication. This element will not display
// unless that information is available.
let user_claims = get_user_claims();
let provider_id = Memo::new(move |_|
user_claims.get()
.as_ref()
.map(|info| info.claims.subject.clone())
);
// Register scholarship server resource
let scholarships = Resource::new(
move || provider_id.get(),
async move |provider_id| {
let Some(provider_id) = provider_id else {
return Ok(Vec::new());
};
// Get the scholarships
get_provider_scholarships(provider_id).await
}
);
// Register scholarship creation/deletion actions
let create_action = ServerAction::::new();
// Button handlers
let create_on_click = move |_| {
// In the future, we'd actually like to create/show a dialog that gets the
// desired name of the scholarship.
// After the request is sent, we'll handle it in an effect.
create_action.dispatch(RegisterScholarship {
provider_id: provider_id.get().unwrap(),
scholarship_name: "Testing Scholarship".to_string()
});
};
// Register scholarship creation effect
Effect::new(move || {
// We want to wait for the action to complete first, but then find whether the request
// succeeded.
if let Some(Ok(_)) = create_action.value().get() {
// Refetch and clear.
scholarships.refetch();
create_action.clear();
}
});
view! {
"Scholarships"
{move || {
scholarships.get().map(|result| {
match result {
Ok(list) => {
let views = list.iter().map(|entry| {
view! {
}
});
Either::Left(views.collect_view())
}
Err(err) => {
Either::Right(view! {
"Failed to load scholarship list: "{err.to_string()}
})
}
}
}).collect_view()
}}
"Create New"
}
}
/// # Scholarship List Entry Component
///
/// Displays a single scholarship within the [`ScholarshipList`] component.
///
/// Example usage (within the [`ScholarshipList`]):
/// ```
/// view! {
///
/// }
/// ```
#[component]
fn ScholarshipListEntry(
/// The scholarship information. This element will attempt to get the name of the scholarship
/// using its `name` field, and also utilizes the `subject` field.
#[prop()] scholarship: ExpandableInfo,
/// A [`Callback<()>`] that runs upon successful deletion of the scholarship.
#[prop(into)] on_delete: Callback<()>,
) -> impl IntoView {
let navigate = use_navigate();
let user_claims = get_user_claims();
let user_id = Memo::new(move |_| {
user_claims.get()
.map(|info| info.claims.subject.clone())
});
// Get the values that we need out of the info. We just need the subject and the name of
// the scholarship.
let name = scholarship.data.get("name")
.unwrap_or(&ValueType::String(None))
.as_string().unwrap_or_default()
.unwrap_or("".to_string());
// Button click handlers
let edit_click = {
let subject = scholarship.subject.clone();
move |_| {
let path = format!("/providers/scholarships/{}", subject);
navigate(path.as_str(), Default::default());
}
};
let delete_click = {
let subject = scholarship.subject.clone();
move |_| {
// Delete the scholarship.
let subject = subject.clone();
log!("Deleting scholarship with ID {:?}", subject);
if let Some(user_id) = user_id.get() {
spawn_local(async move {
match delete_provider_scholarship(
user_id,
subject
).await {
Ok(_) => on_delete.run(()),
_ => {}
}
});
}
}
};
view! {
{name}
"Edit"
"Delete"
}
}
#[component]
fn ScholarshipForm(
#[prop(into)] scholarship_id: Signal
}
>
{move || {
scholarship_info.get()
.map(|res_scholarship| {
match res_scholarship {
Ok(scholarship) => {
let reactive_info = scholarship.as_reactive();
Either::Left(view! {
"Scholarship Info Form (test)"
"Submit"
{result_msg}
})
}
Err(err) => {
Either::Right(view! {
"Error while getting scholarship: "{err.to_string()}