freezone/platform/src/components/layout/sidebar.rs
2025-06-30 15:49:32 +02:00

229 lines
11 KiB
Rust

use yew::prelude::*;
use crate::routing::{AppView, ViewContext};
#[derive(Properties, PartialEq)]
pub struct SidebarProps {
pub current_view: AppView,
pub current_context: ViewContext,
pub is_visible: bool,
pub on_view_change: Callback<AppView>,
}
#[function_component(Sidebar)]
pub fn sidebar(props: &SidebarProps) -> Html {
let current_view = props.current_view.clone();
let current_context = props.current_context.clone();
let on_view_change = props.on_view_change.clone();
let sidebar_class = if props.is_visible {
"sidebar shadow-sm border-end d-flex show"
} else {
"sidebar shadow-sm border-end d-flex"
};
// All possible navigation items
let all_nav_items = vec![
AppView::Home,
AppView::Administration,
AppView::PersonAdministration,
AppView::Accounting,
AppView::Contracts,
AppView::Governance,
AppView::Treasury,
AppView::Entities,
];
// Filter items based on current context
let nav_items: Vec<AppView> = all_nav_items
.into_iter()
.filter(|view| view.is_available_for_context(&current_context))
.collect();
html! {
<div class={sidebar_class} id="sidebar">
<div class="py-2">
// Identity Card - Business/Residence
<div class="px-3 mb-3">
{match current_context {
ViewContext::Business => {
let business_view = AppView::Business;
let is_active = business_view == current_view;
let on_click = {
let on_view_change = on_view_change.clone();
Callback::from(move |_: MouseEvent| {
on_view_change.emit(AppView::Business);
})
};
html! {
<div
class={classes!(
"card",
"shadow-sm",
if is_active { "bg-dark text-white border-0" } else { "bg-white border-dark" },
"cursor-pointer"
)}
onclick={on_click}
style="cursor: pointer;"
>
<div class="card-body p-3">
<div class="d-flex align-items-center">
<div class={classes!(
"rounded-circle",
"d-flex",
"align-items-center",
"justify-content-center",
"me-3",
if is_active { "bg-white text-dark" } else { "bg-dark text-white" }
)} style="width: 40px; height: 40px;">
<i class="bi bi-building fs-5"></i>
</div>
<div class="flex-grow-1">
<h6 class="mb-0">{"TechCorp Solutions"}</h6>
<small class={classes!(
"font-monospace",
if is_active { "text-white-50" } else { "text-muted" }
)}>
{"BIZ-2024-001"}
</small>
</div>
</div>
</div>
</div>
}
},
ViewContext::Person => {
let residence_view = AppView::Residence;
let is_active = residence_view == current_view;
let on_click = {
let on_view_change = on_view_change.clone();
Callback::from(move |_: MouseEvent| {
on_view_change.emit(AppView::Residence);
})
};
html! {
<div
class={classes!(
"card",
"shadow-sm",
if is_active { "bg-dark text-white border-0" } else { "bg-white border-dark" },
"cursor-pointer"
)}
onclick={on_click}
style="cursor: pointer;"
>
<div class="card-body p-3">
<div class="d-flex align-items-center">
<div class={classes!(
"rounded-circle",
"d-flex",
"align-items-center",
"justify-content-center",
"me-3",
if is_active { "bg-white text-dark" } else { "bg-dark text-white" }
)} style="width: 40px; height: 40px;">
<i class="bi bi-person fs-5"></i>
</div>
<div class="flex-grow-1">
<h6 class="mb-0">{"Timur Gordon"}</h6>
<small class={classes!(
"font-monospace",
if is_active { "text-white-50" } else { "text-muted" }
)}>
{"RES-ZNZ-2024-042"}
</small>
</div>
</div>
</div>
</div>
}
}
}}
</div>
// Horizontal divider
<div class="px-3 mb-3">
<hr class="text-muted" />
</div>
// Navigation items
<ul class="nav flex-column">
{for nav_items.iter().map(|view| {
let is_active = *view == current_view;
let view_to_emit = view.clone();
let on_click = {
let on_view_change = on_view_change.clone();
Callback::from(move |_: MouseEvent| {
on_view_change.emit(view_to_emit.clone());
})
};
html! {
<li class="nav-item">
<a
class={classes!(
"nav-link",
"d-flex",
"align-items-center",
"ps-3",
"py-2",
is_active.then_some("active"),
is_active.then_some("fw-bold"),
is_active.then_some("border-start"),
is_active.then_some("border-4"),
is_active.then_some("border-primary"),
)}
href="#"
onclick={on_click}
>
<i class={classes!("bi", view.get_icon(), "me-2")}></i>
{view.get_title(&current_context)}
</a>
</li>
}
})}
</ul>
// Divider for external applications
<div class="px-3 my-3">
<hr class="text-muted" />
</div>
// External Applications
<div class="px-3">
// Marketplace Button
<div class="card border-primary mb-3" style="cursor: pointer;">
<div class="card-body p-3">
<div class="d-flex align-items-center">
<div class="rounded-circle bg-primary text-white d-flex align-items-center justify-content-center me-3" style="width: 35px; height: 35px;">
<i class="bi bi-shop fs-6"></i>
</div>
<div class="flex-grow-1">
<h6 class="mb-1 text-primary">{"Marketplace"}</h6>
<small class="text-muted">{"Browse contract templates"}</small>
</div>
</div>
</div>
</div>
// DeFi Button
<div class="card border-success" style="cursor: pointer;">
<div class="card-body p-3">
<div class="d-flex align-items-center">
<div class="rounded-circle bg-success text-white d-flex align-items-center justify-content-center me-3" style="width: 35px; height: 35px;">
<i class="bi bi-currency-exchange fs-6"></i>
</div>
<div class="flex-grow-1">
<h6 class="mb-1 text-success">{"DeFi"}</h6>
<small class="text-muted">{"Financial tools & escrow"}</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
}
}