diff --git a/src/app.rs b/src/app.rs index ec4ff85..87f4682 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,4 +1,4 @@ -use crate::pages::{ProviderPortal, ScholarshipInfoPage, ProviderContactPage, LoanerShell, LoanerFallback, LoanerBorrowForm, LoanerReturnForm, AuthCallbackPage, ApplicantsPageFallback, AdminShell, AdminTesting, AdminFallback}; +use crate::pages::{ProviderPortal, ScholarshipInfoPage, ProviderContactPage, LoanerShell, LoanerFallback, LoanerBorrowForm, LoanerReturnForm, AuthCallbackPage, ApplicantsPageFallback, AdminShell, AdminHomePage, AdminProviderPage, AdminScholarshipPage}; use leptos::prelude::*; use leptos_meta::{MetaTags, Stylesheet, Title, provide_meta_context}; use leptos_oidc::AuthSignal; @@ -106,8 +106,9 @@ fn LoanerRoutes() -> impl MatchNestedRoutes + Clone { fn AdminRoutes() -> impl MatchNestedRoutes + Clone { view! { - - + + + } .into_inner() diff --git a/src/common/tab_info.rs b/src/common/tab_info.rs index f292629..f2d0872 100644 --- a/src/common/tab_info.rs +++ b/src/common/tab_info.rs @@ -1,5 +1,14 @@ #[derive(Debug, Clone)] pub struct TabInfo { - pub name: String, + pub text: String, pub path: String, +} + +impl TabInfo { + pub fn new(text: impl Into, path: impl Into) -> Self { + Self { + text: text.into(), + path: path.into(), + } + } } \ No newline at end of file diff --git a/src/components/lists/multi/mod.rs b/src/components/lists/multi/mod.rs index c3fcbbc..717cb89 100644 --- a/src/components/lists/multi/mod.rs +++ b/src/components/lists/multi/mod.rs @@ -1,6 +1,8 @@ mod utils; mod checkbox; mod chips; +mod select_nav_list; pub use checkbox::*; -pub use chips::*; \ No newline at end of file +pub use chips::*; +pub use select_nav_list::*; \ No newline at end of file diff --git a/src/components/lists/multi/select_nav_list.rs b/src/components/lists/multi/select_nav_list.rs new file mode 100644 index 0000000..a6afdf9 --- /dev/null +++ b/src/components/lists/multi/select_nav_list.rs @@ -0,0 +1,154 @@ +use leptos::logging::log; +use leptos::prelude::*; +use leptos_router::components::A; +use crate::components::utils; + +#[derive(Debug, Clone)] +pub struct NavListInfo { + pub text: String, + pub id: String, + pub slug: String, +} + +#[component] +fn NavListItem( + #[prop(into)] name: Signal, + #[prop(into)] value: Signal, + #[prop(into)] text: Signal, + #[prop(into)] slug: Signal, + #[prop(into)] selected: Signal, + #[prop(into)] on_change: Callback<()>, +) -> impl IntoView { + view! { + + } +} + +#[component] +fn NavListCheckbox( + #[prop(into)] selected: Signal, + #[prop(into)] on_change: Callback<()>, + #[prop(into)] name: Signal, + #[prop(into)] value: Signal, +) -> impl IntoView { + let id = Memo::new(move |_| utils::create_unique_id(&name.get(), &value.get())); + + view! { + + } +} + +/// # Selectable Nav-List Component +/// +/// This component takes a `Vec` to show a list. The list contains links that append +/// the `NavListItem::slug` to the current path, and each item is selectable. The list of selected +/// items is usable in this component's parent. +#[component] +pub fn SelectableNavList( + #[prop(into)] selected: RwSignal>, + #[prop(into)] items: Signal>, + #[prop(into)] name: Signal, +) -> impl IntoView { + let all_selected = RwSignal::new(false); + let all_selected_change = move || { + if all_selected.get() { + log!("Unchecking all values."); + all_selected.set(false); + selected.set(Vec::new()); + } else { + log!("Checking all values."); + all_selected.set(true); + selected.set( + items + .get() + .iter() + .map(|info| info.id.clone()) + .collect::>() + ); + } + }; + + view! { +
+ // Header block - contains evenly spaced headers and the "select all" checkbox. + // Include a checkbox here that's got the same style as the items. This maintains spacing. + // All headers *must* be evenly spaced, otherwise the list starts getting confusing. +
+ +
+ // Items block - contains constructed items. These are selectable. + + } + } + /> +
+ } +} diff --git a/src/components/tabs/sidebar.rs b/src/components/tabs/sidebar.rs index 1155626..bc9ee53 100644 --- a/src/components/tabs/sidebar.rs +++ b/src/components/tabs/sidebar.rs @@ -5,7 +5,7 @@ use crate::common::TabInfo; #[component] fn SidebarTab( - #[prop(into)] name: Signal, + #[prop(into)] text: Signal, #[prop(into)] path: Signal ) -> impl IntoView { let location = use_location(); @@ -24,12 +24,55 @@ fn SidebarTab( class=(["bg-transparent", "text-white"], move || !selected.get()) class=(["bg-white", "text-black"], move || selected.get()) > - {name} + {text} } } +/// # Tab Sidebar Component +/// +/// This component creates a list of tabs that correspond to the given `Vec`. +/// There are a few things to note: +/// +/// - This component defines an [`Outlet`], meaning that it is designed to display the route +/// information on its own. If you intend to use this on a page, the page should not also contain +/// an `Outlet` component unless it needs to render its own nested children. +/// - The "selected" appearance for the individual tabs work best when their paths are at the same +/// depth. For example: +/// ``` +/// let good_list = vec! [ +/// TabInfo { +/// ..., +/// path: "/default/path" +/// }, +/// TabInfo { +/// ..., +/// path: "/good/path" +/// } +/// ]; +/// +/// let bad_list = vec! [ +/// TabInfo { +/// ..., +/// path: "/default/path" +/// }, +/// TabInfo { +/// ..., +/// path: "/bad/path/here" +/// } +/// ] +/// ``` +/// +/// Example usage in a view (assuming the good list from above): +/// ``` +/// view! { +/// +/// // Remember, no Outlets! +/// } +/// ``` #[component] pub fn TabSidebarList( #[prop(into)] tabs: Signal>, @@ -41,10 +84,10 @@ pub fn TabSidebarList( diff --git a/src/pages/admin/home_page.rs b/src/pages/admin/home_page.rs new file mode 100644 index 0000000..b0f0f45 --- /dev/null +++ b/src/pages/admin/home_page.rs @@ -0,0 +1,11 @@ +use leptos::prelude::*; + +#[component] +pub fn AdminHomePage() -> impl IntoView { + view! { +
+
"Admin Home Page"
+
"This is just a placeholder for now."
+
+ } +} diff --git a/src/pages/admin/mod.rs b/src/pages/admin/mod.rs index d805db1..d5400ea 100644 --- a/src/pages/admin/mod.rs +++ b/src/pages/admin/mod.rs @@ -1,2 +1,8 @@ mod shell; -pub use shell::*; \ No newline at end of file +pub use shell::*; +mod home_page; +pub use home_page::*; +mod provider_page; +pub use provider_page::*; +mod scholarship_page; +pub use scholarship_page::*; diff --git a/src/pages/admin/provider_page.rs b/src/pages/admin/provider_page.rs new file mode 100644 index 0000000..98ab694 --- /dev/null +++ b/src/pages/admin/provider_page.rs @@ -0,0 +1,30 @@ +use leptos::prelude::*; +use crate::components::{NavListInfo, SelectableNavList}; + +#[component] +pub fn AdminProviderPage() -> impl IntoView { + let selected_items = RwSignal::new(Vec::new()); + let provider_list = RwSignal::new(vec![ + NavListInfo { + text: "Testing 1".to_string(), + id: "1".to_string(), + slug: "1".to_string(), + }, + NavListInfo { + text: "Testing 2".to_string(), + id: "2".to_string(), + slug: "2".to_string(), + } + ]); + + view! { +
+
"Provider View"
+ +
+ } +} diff --git a/src/pages/admin/scholarship_page.rs b/src/pages/admin/scholarship_page.rs new file mode 100644 index 0000000..c3b13ab --- /dev/null +++ b/src/pages/admin/scholarship_page.rs @@ -0,0 +1,15 @@ +use leptos::prelude::*; + +#[component] +pub fn AdminScholarshipPage() -> impl IntoView { + view! { +
+
+ "Scholarships View" +
+
+ "This is a placeholder for now. It will show a list of all scholarships." +
+
+ } +} \ No newline at end of file diff --git a/src/pages/admin/shell.rs b/src/pages/admin/shell.rs index 93ad9c3..56aacbb 100644 --- a/src/pages/admin/shell.rs +++ b/src/pages/admin/shell.rs @@ -15,14 +15,9 @@ pub fn AdminShell() -> impl IntoView { // We'll put the tabs here. }