feat: create sidebar tabs list

This commit is contained in:
alphafetus committed 2026-01-22 01:34:58 -05:00
1 parent 9e418dddf5
commit 4a2a5ca9ff
12 files changed
+158 -7

No files matched your search

+14 -1
View File
@@ -1,4 +1,4 @@
use crate::pages::{ProviderPortal, ScholarshipInfoPage, ProviderContactPage, LoanerShell, LoanerFallback, LoanerBorrowForm, LoanerReturnForm, AuthCallbackPage, ApplicantsPageFallback, TestPage};
use crate::pages::{ProviderPortal, ScholarshipInfoPage, ProviderContactPage, LoanerShell, LoanerFallback, LoanerBorrowForm, LoanerReturnForm, AuthCallbackPage, ApplicantsPageFallback, AdminShell, AdminTesting, AdminFallback};
use leptos::prelude::*;
use leptos_meta::{MetaTags, Stylesheet, Title, provide_meta_context};
use leptos_oidc::AuthSignal;
@@ -64,6 +64,7 @@ pub fn AppWithRoutes() -> impl IntoView {
// <Route path=path!("/testing") view=TestPage />
<ProviderRoutes />
<LoanerRoutes />
<AdminRoutes />
</Routes>
</ToastList>
</main>
@@ -101,6 +102,18 @@ fn LoanerRoutes() -> impl MatchNestedRoutes + Clone {
.into_any_nested_route()
}
#[component(transparent)]
fn AdminRoutes() -> impl MatchNestedRoutes + Clone {
view! {
<ParentRoute path=path!("admin") view=AdminShell>
<Route path=path!("home") view=AdminFallback />
<Route path=path!("testing") view=AdminTesting />
</ParentRoute>
}
.into_inner()
.into_any_nested_route()
}
#[component]
pub fn AuthErrorView() -> impl IntoView {
let auth = use_context::<AuthSignal>().expect("AuthError: RwSignal<AuthStore> was not found");
+2 -1
View File
@@ -7,8 +7,9 @@ mod submit_status;
mod user;
mod value_type;
pub mod animations;
mod tab_info;
pub use self::{
comparison::*, date_info::*, expandable_info::*, form_params::*, form_schema::*,
submit_status::*, user::*, value_type::*,
submit_status::*, user::*, value_type::*, tab_info::*,
};
+5
View File
@@ -0,0 +1,5 @@
#[derive(Debug, Clone)]
pub struct TabInfo {
pub name: String,
pub path: String,
}
+2 -2
View File
@@ -22,7 +22,7 @@ pub fn Banner(
title: String,
/// The file path or URL of a logo displayed at the top left of the banner.
#[prop(into)]
logo: String,
logo: Signal<String>,
/// The route that the navigates user to a page (home page).
#[prop(into, optional)]
path: Signal<String>,
@@ -44,7 +44,7 @@ pub fn Banner(
<div class="flex flex-row items-center gap-4 p-6 pb-2">
<div class="flex-shrink-0">
<img
src=logo.clone()
src=logo
class="h-10 w-10 cursor-pointer"
alt="icon"
on:click=on_click
+18
View File
@@ -0,0 +1,18 @@
use leptos::prelude::*;
use crate::components::login::setup::provide_auth_context;
#[component]
pub fn AdminLoginContext(
children: Children
) -> impl IntoView {
provide_auth_context(
crate::utils::use_origin(),
"https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Lfjuy5zaM",
"56c2bqvl021rv8d5mq36blt7jv",
"/providers/callback"
);
view! {
{children()}
}
}
+3 -1
View File
@@ -1,5 +1,7 @@
pub(crate) mod setup;
mod provider;
pub use provider::*;
mod student;
pub use student::*;
pub(crate) mod setup;
mod admin;
pub use admin::*;
+2 -1
View File
@@ -13,9 +13,10 @@ mod lists;
mod toasts;
mod header;
pub mod login;
mod tabs;
pub use self::{
action_button::*, banner::*, dashboard_button::*, lists::*,
loading::*, multi_entry::*, panel::*, row::*, text_field::*,
validated_form::*, toasts::*, header::*, date::*
validated_form::*, toasts::*, header::*, date::*, tabs::*
};
+2
View File
@@ -0,0 +1,2 @@
mod sidebar;
pub use sidebar::*;
+57
View File
@@ -0,0 +1,57 @@
use leptos::prelude::*;
use leptos_router::components::{Outlet, A};
use leptos_router::hooks::use_location;
use crate::common::TabInfo;
#[component]
fn SidebarTab(
#[prop(into)] name: Signal<String>,
#[prop(into)] path: Signal<String>
) -> impl IntoView {
let location = use_location();
let selected = Memo::new(move |_| {
let current_path = location.pathname.get();
// There's extra logic here as well, but for now this works.
current_path.starts_with(&path.get())
});
view! {
<A href={move || path.get()}>
<div class="p-3 transition-all font-bold"
class=(["bg-transparent", "text-white"], move || !selected.get())
class=(["bg-white", "text-black"], move || selected.get())>
{name}
</div>
</A>
}
}
#[component]
pub fn TabSidebarList(
#[prop(into)] tabs: Signal<Vec<TabInfo>>,
) -> impl IntoView {
// Create a series of tabs based on a given list of names and paths.
view! {
<div class="flex flex-row h-full w-full fixed">
<nav class="flex flex-col bg-red-900 w-64 h-full overflow-scroll">
<For
each=move || tabs.get()
key=|info| info.name.clone()
let(TabInfo {name, path})
>
<SidebarTab
name=name
path=path
/>
</For>
</nav>
<div class="flex flex-1">
<Outlet />
</div>
</div>
}
}
+2
View File
@@ -0,0 +1,2 @@
mod shell;
pub use shell::*;
+49
View File
@@ -0,0 +1,49 @@
use leptos::prelude::*;
use crate::common::TabInfo;
use crate::components::{Banner, TabSidebarList};
use crate::components::login::AdminLoginContext;
/// # Admin Shell Component
///
/// This shell should keep track of pretty much everything that changes across the admin page. It
/// will provide the login context, the banner, and the tabs.
#[component]
pub fn AdminShell() -> impl IntoView {
// Create login context
view! {
<AdminLoginContext>
<Banner title="R15 Scholarships Admin" logo="/PHS_Stacked_Acronym.png" />
// We'll put the tabs here.
<TabSidebarList
tabs=vec![
TabInfo {
name: "Home".to_string(),
path: "/admin/home".to_string(),
},
TabInfo {
name: "Test".to_string(),
path: "/admin/testing".to_string(),
}
]
/>
</AdminLoginContext>
}
}
#[component]
pub fn AdminFallback() -> impl IntoView {
view! {
<div>
<p class="mx-auto">"This is a fallback page"</p>
</div>
}
}
#[component]
pub fn AdminTesting() -> impl IntoView {
view! {
<div>
<p class="mx-auto">"This is a testing page"</p>
</div>
}
}
+2 -1
View File
@@ -10,9 +10,10 @@ mod unauthenticated_page;
mod provider_contact;
mod auth_callback;
mod provider_applicants;
mod admin;
pub use self::{
about_page::*, comparison_test::*, home_page::*, loaner_page::*, provider_portal::*,
test_page::*, unauthenticated_page::*, scholarship_info::*, provider_contact::*,
auth_callback::*, provider_applicants::*
auth_callback::*, provider_applicants::*, admin::*
};