Header & Sidebar User Controls
The header and sidebar split responsibilities: the header handles global actions (dialpad, notifications, assistant toggle), while the sidebar owns all user-related controls through a popover menu.
Header Component
Section titled “Header Component”The header renders the top bar with a hamburger toggle, action buttons, and the assistant trigger.
#[component]pub fn Header( #[props(default = false)] is_assistant_open: bool, #[props(default = false)] can_use_assistant: bool, on_toggle_assistant: Option<EventHandler<()>>, sidebar_collapsed: Option<Signal<bool>>, header_actions: Option<Element>,) -> Element| Prop | Type | Description |
|---|---|---|
is_assistant_open | bool | Whether the assistant panel is visible |
can_use_assistant | bool | Gates the assistant toggle button |
on_toggle_assistant | Option<EventHandler<()>> | Fires when the user clicks the assistant icon |
sidebar_collapsed | Option<Signal<bool>> | Controls the hamburger menu state |
header_actions | Option<Element> | Slot for custom action buttons |
The header renders actions in this order: DialPad → NotificationBell → Assistant.
Assistant Icon
Section titled “Assistant Icon”The assistant toggle uses a fully circular button with a breathing ring animation:
// Closed state"rounded-full assistant-trigger-ring"
// Open state"rounded-full bg-primary/10 shadow-md ring-2 ring-primary/30"The assistant-trigger-ring class in tailwind.css applies a 2.5-second infinite ease-in-out animation with an amber glow. The icon color switches from text-muted-foreground (closed) to primary (open).
Sidebar User Menu
Section titled “Sidebar User Menu”The sidebar renders a user menu at its bottom edge. Clicking it opens an upward popover with settings, theme toggle, and logout.
Internal Signals
Section titled “Internal Signals”let mut is_user_menu_open = use_signal(|| false);let mut is_light = use_signal(|| false);| Signal | Type | Purpose |
|---|---|---|
is_user_menu_open | Signal<bool> | Controls popover visibility |
is_light | Signal<bool> | Tracks current theme, synced from DOM on mount |
On mount, a use_effect reads the data-theme attribute from <html> to initialize is_light.
User Menu Layout
Section titled “User Menu Layout”Collapsed sidebar — shows only an avatar circle (first letter of the user’s name).
Expanded sidebar — shows the avatar, full name, and a ChevronUp icon.
Clicking either state toggles is_user_menu_open.
Popover Actions
Section titled “Popover Actions”The popover opens upward with a backdrop at z-[999] and the panel at z-[1000]:
| Action | Behavior |
|---|---|
| Settings | Navigates to /settings via nav.push(Route::SettingsView {}) |
| Theme toggle | Swaps is_light, updates data-theme on <html>, saves to localStorage key "loquent-theme" |
| Logout | Collapses sidebar, closes popover, calls on_logout() → logout_api() → redirects to /login |
Theme Toggle Logic
Section titled “Theme Toggle Logic”// Toggle handler inside the popoverlet new_theme = if *is_light.read() { "dark" } else { "light" };is_light.set(new_theme == "light");
// Updates DOM attribute and localStoragedocument.set_attribute("data-theme", new_theme);localStorage.set_item("loquent-theme", new_theme);The theme persists across sessions through localStorage. On page load, the app reads loquent-theme and applies it before first paint.
| File | Role |
|---|---|
src/shared/components/header.rs | Header bar with actions and assistant toggle |
src/shared/components/sidebar.rs | Sidebar with navigation groups and user menu |
src/shared/layouts/app_layout.rs | Wires header and sidebar into the main layout |
tailwind.css | Assistant breathing ring animation styles |