feat: added oauth based login and registration (#1)

Co-authored-by: Sharang Parnerkar <parnerkarsharang@gmail.com>
Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
2026-02-18 09:21:46 +00:00
parent b8aad0e23c
commit 1072770d11
36 changed files with 7420 additions and 26 deletions

View File

@@ -0,0 +1,23 @@
use dioxus::prelude::*;
use crate::components::sidebar::Sidebar;
use crate::Route;
/// Application shell layout that wraps all authenticated pages.
///
/// Renders a fixed sidebar on the left and the active child route
/// in the scrollable main content area via `Outlet`.
#[component]
pub fn AppShell() -> Element {
rsx! {
div { class: "app-shell",
Sidebar {
email: "user@example.com".to_string(),
avatar_url: String::new(),
}
main { class: "main-content",
Outlet::<Route> {}
}
}
}
}

25
src/components/card.rs Normal file
View File

@@ -0,0 +1,25 @@
use dioxus::prelude::*;
/// Reusable dashboard card with icon, title, description and click-through link.
///
/// # Arguments
///
/// * `title` - Card heading text.
/// * `description` - Short description shown beneath the title.
/// * `href` - URL the card links to when clicked.
/// * `icon` - Element rendered as the card icon (typically a `dioxus_free_icons::Icon`).
#[component]
pub fn DashboardCard(
title: String,
description: String,
href: String,
icon: Element,
) -> Element {
rsx! {
a { class: "dashboard-card", href: "{href}",
div { class: "card-icon", {icon} }
h3 { class: "card-title", "{title}" }
p { class: "card-description", "{description}" }
}
}
}

15
src/components/login.rs Normal file
View File

@@ -0,0 +1,15 @@
use crate::Route;
use dioxus::prelude::*;
#[component]
pub fn Login(redirect_url: String) -> Element {
let navigator = use_navigator();
use_effect(move || {
let target = format!("/auth?redirect_url={}", redirect_url);
navigator.push(NavigationTarget::<Route>::External(target));
});
rsx!(
div { class: "text-center p-6", "Redirecting to secure login page…" }
)
}

8
src/components/mod.rs Normal file
View File

@@ -0,0 +1,8 @@
mod app_shell;
mod card;
mod login;
pub mod sidebar;
pub use app_shell::*;
pub use card::*;
pub use login::*;

154
src/components/sidebar.rs Normal file
View File

@@ -0,0 +1,154 @@
use dioxus::prelude::*;
use dioxus_free_icons::icons::bs_icons::{
BsBoxArrowRight, BsFileEarmarkText, BsGear, BsGithub, BsGrid,
BsHouseDoor, BsRobot,
};
use dioxus_free_icons::icons::fa_solid_icons::FaCubes;
use dioxus_free_icons::Icon;
use crate::Route;
/// Navigation entry for the sidebar.
struct NavItem {
label: &'static str,
route: Route,
/// Bootstrap icon element rendered beside the label.
icon: Element,
}
/// Fixed left sidebar containing header, navigation, logout, and footer.
///
/// # Arguments
///
/// * `email` - Email address displayed beneath the avatar placeholder.
/// * `avatar_url` - URL for the avatar image (unused placeholder for now).
#[component]
pub fn Sidebar(email: String, avatar_url: String) -> Element {
let nav_items: Vec<NavItem> = vec![
NavItem {
label: "Overview",
route: Route::OverviewPage {},
icon: rsx! { Icon { icon: BsHouseDoor, width: 18, height: 18 } },
},
NavItem {
label: "Documentation",
route: Route::OverviewPage {},
icon: rsx! { Icon { icon: BsFileEarmarkText, width: 18, height: 18 } },
},
NavItem {
label: "Agents",
route: Route::OverviewPage {},
icon: rsx! { Icon { icon: BsRobot, width: 18, height: 18 } },
},
NavItem {
label: "Models",
route: Route::OverviewPage {},
icon: rsx! { Icon { icon: FaCubes, width: 18, height: 18 } },
},
NavItem {
label: "Settings",
route: Route::OverviewPage {},
icon: rsx! { Icon { icon: BsGear, width: 18, height: 18 } },
},
];
// Determine current path to highlight the active nav link.
let current_route = use_route::<Route>();
rsx! {
aside { class: "sidebar",
// -- Header: avatar circle + email --
SidebarHeader { email: email.clone(), avatar_url }
// -- Navigation links --
nav { class: "sidebar-nav",
for item in nav_items {
{
// Simple active check: highlight Overview only when on `/`.
let is_active = item.route == current_route;
let cls = if is_active {
"sidebar-link active"
} else {
"sidebar-link"
};
rsx! {
Link {
to: item.route,
class: cls,
{item.icon}
span { "{item.label}" }
}
}
}
}
}
// -- Logout button --
div { class: "sidebar-logout",
Link {
to: NavigationTarget::<Route>::External("/auth/logout".into()),
class: "sidebar-link logout-btn",
Icon { icon: BsBoxArrowRight, width: 18, height: 18 }
span { "Logout" }
}
}
// -- Footer: version + social links --
SidebarFooter {}
}
}
}
/// Avatar circle and email display at the top of the sidebar.
///
/// # Arguments
///
/// * `email` - User email to display.
/// * `avatar_url` - Placeholder for future avatar image URL.
#[component]
fn SidebarHeader(email: String, avatar_url: String) -> Element {
// Extract initials from email (first two chars before @).
let initials: String = email
.split('@')
.next()
.unwrap_or("U")
.chars()
.take(2)
.collect::<String>()
.to_uppercase();
rsx! {
div { class: "sidebar-header",
div { class: "avatar-circle",
span { class: "avatar-initials", "{initials}" }
}
p { class: "sidebar-email", "{email}" }
}
}
}
/// Footer section with version string and placeholder social links.
#[component]
fn SidebarFooter() -> Element {
let version = env!("CARGO_PKG_VERSION");
rsx! {
footer { class: "sidebar-footer",
div { class: "sidebar-social",
a {
href: "#",
class: "social-link",
title: "GitHub",
Icon { icon: BsGithub, width: 16, height: 16 }
}
a {
href: "#",
class: "social-link",
title: "Impressum",
Icon { icon: BsGrid, width: 16, height: 16 }
}
}
p { class: "sidebar-version", "v{version}" }
}
}
}