Files
webappAIO/frontend/src/components/desktop/Navbar.tsx
T
ghassan 84cd474bcb Rebuild desktop shell on the real AdminLTE 4 package
Install [email protected] (+ bootstrap 5.3.8, bootstrap-icons as peer deps) and
rebuild every desktop component against actual AdminLTE markup pulled from
its live docs/demo, rather than a Tailwind approximation:

- Real app-wrapper/app-header/app-sidebar/app-main/app-footer structure
- Working PushMenu (sidebar collapse), Treeview, ColorMode (light/dark/auto)
- Navbar: functional search (wired to Users list ?search=), notifications
  dropdown (MQTT-driven), user menu dropdown
- Sidebar: grouped nav-header sections, active-route highlighting
- Breadcrumbs, footer, Bootstrap card/table/pagination/progress widgets

Tailwind is now imported utilities-only (no preflight) so it coexists with
Bootstrap's reset; mobile shell and modals are untouched and still Tailwind.
2026-08-04 13:47:02 +03:00

133 lines
5.1 KiB
TypeScript

import { useState, type FormEvent } from 'react'
import { Link, useNavigate } from 'react-router-dom'
import { useAuth } from '../../features/auth/AuthContext'
import { useNotifications } from './useNotifications'
export function Navbar() {
const { user, logout } = useAuth()
const navigate = useNavigate()
const [search, setSearch] = useState('')
const notifications = useNotifications()
function handleSearch(e: FormEvent) {
e.preventDefault()
if (search.trim()) {
navigate(`/users?search=${encodeURIComponent(search.trim())}`)
}
}
return (
<nav className="app-header navbar navbar-expand bg-body">
<div className="container-fluid">
<ul className="navbar-nav">
<li className="nav-item">
<a className="nav-link" data-lte-toggle="sidebar" href="#" role="button" aria-label="Toggle sidebar">
<i className="bi bi-list"></i>
</a>
</li>
</ul>
<form className="d-none d-md-flex ms-3" role="search" onSubmit={handleSearch}>
<input
type="search"
value={search}
onChange={(e) => setSearch(e.target.value)}
className="form-control form-control-sm"
placeholder="Search users…"
aria-label="Search"
/>
</form>
<ul className="navbar-nav ms-auto">
<li className="nav-item dropdown">
<a
className="nav-link"
data-bs-toggle="dropdown"
href="#"
aria-label={`Notifications: ${notifications.length} unread`}
>
<i className="bi bi-bell-fill"></i>
{notifications.length > 0 && (
<span className="navbar-badge badge text-bg-warning">{notifications.length}</span>
)}
</a>
<div className="dropdown-menu dropdown-menu-lg dropdown-menu-end">
<span className="dropdown-item dropdown-header">{notifications.length} Notifications</span>
<div className="dropdown-divider"></div>
{notifications.length === 0 ? (
<span className="dropdown-item text-secondary">No new notifications</span>
) : (
notifications.map((n, i) => (
<div key={i}>
<a href="#" className="dropdown-item">
<i className="bi bi-person-fill me-2"></i>
{n.message}
</a>
{i < notifications.length - 1 && <div className="dropdown-divider"></div>}
</div>
))
)}
</div>
</li>
<li className="nav-item dropdown">
<a
className="nav-link"
href="#"
id="bd-theme"
aria-label="Toggle color scheme"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<i className="bi bi-sun-fill d-none" data-lte-theme-icon="light"></i>
<i className="bi bi-moon-fill" data-lte-theme-icon="dark"></i>
<i className="bi bi-circle-half d-none" data-lte-theme-icon="auto"></i>
</a>
<ul className="dropdown-menu dropdown-menu-end" aria-labelledby="bd-theme">
<li>
<button type="button" className="dropdown-item d-flex align-items-center" data-bs-theme-value="light">
<i className="bi bi-sun-fill me-2"></i>Light
</button>
</li>
<li>
<button type="button" className="dropdown-item d-flex align-items-center" data-bs-theme-value="dark">
<i className="bi bi-moon-fill me-2"></i>Dark
</button>
</li>
<li>
<button type="button" className="dropdown-item d-flex align-items-center" data-bs-theme-value="auto">
<i className="bi bi-circle-half me-2"></i>Auto
</button>
</li>
</ul>
</li>
<li className="nav-item dropdown user-menu">
<a href="#" className="nav-link dropdown-toggle" data-bs-toggle="dropdown">
<i className="bi bi-person-circle fs-5"></i>
<span className="d-none d-md-inline ms-1">{user?.name}</span>
</a>
<ul className="dropdown-menu dropdown-menu-lg dropdown-menu-end">
<li className="user-header text-bg-primary">
<i className="bi bi-person-circle" style={{ fontSize: '3rem' }}></i>
<p>
{user?.name}
<small>{user?.roles.join(', ')}</small>
</p>
</li>
<li className="user-footer">
<Link to="/settings/2fa" className="btn btn-outline-secondary">
2FA Settings
</Link>
<button onClick={() => logout()} className="btn btn-outline-danger float-end">
Sign out
</button>
</li>
</ul>
</li>
</ul>
</div>
</nav>
)
}