Ghassan Yusuf 2a2fac9cdf Add Roles/MQTT-settings/Backup pages, AdminLTE demo shells, and a separate mobile design system
Backend: real endpoints for role-permission overview, MQTT broker settings
(with live reachability check), and SQLite backup download/restore (new
system.manage permission, super-admin only; restore validates the SQLite
file header and keeps a pre-restore safety copy).

Desktop frontend: Roles (read-only permissions view), MQTT Settings,
Backup & Restore wired to those endpoints; plus static AdminLTE-matching
shells for Profile, Settings (embeds the real 2FA flow in its Security
tab), Invoice, Calendar (FullCalendar), Chat, File Manager, and Projects.

Mobile frontend: new frontend/src/styles/mobile.css, a from-scratch design
system (purple theme, cards, slide-out sidebar, bottom tab bar) scoped
entirely under a .mob-app root and mob- prefixed classes so it can never
collide with the desktop Bootstrap/AdminLTE styling. Rebuilt TopBar/
BottomNav, added a new SlideOutNav, and reskinned the Dashboard and Users
pages to it with real data.
2026-08-04 14:51:28 +03:00

49 lines
1.6 KiB
TypeScript

import { NavLink } from 'react-router-dom'
import { NAV_GROUPS } from '../../layouts/shell/navConfig'
import { usePermission } from '../../hooks/usePermission'
interface SlideOutNavProps {
open: boolean
onClose: () => void
}
export function SlideOutNav({ open, onClose }: SlideOutNavProps) {
const { can } = usePermission()
return (
<>
<div className={`mob-sidebar-backdrop ${open ? 'mob-show' : ''}`} onClick={onClose} />
<nav className={`mob-sidebar ${open ? 'mob-open' : ''}`} aria-label="Main navigation">
<div className="mob-sidebar-header">
<span className="mob-sidebar-title">webappAIO</span>
<button className="mob-sidebar-close" onClick={onClose} aria-label="Close menu">
</button>
</div>
{NAV_GROUPS.map((group) => {
const items = group.items.filter((item) => !item.permission || can(item.permission))
if (items.length === 0) return null
return (
<div key={group.header}>
<div className="mob-sidebar-section-label">{group.header}</div>
{items.map((item) => (
<NavLink
key={item.to}
to={item.to}
onClick={onClose}
className={({ isActive }) => `mob-sidebar-link ${isActive ? 'mob-active' : ''}`}
>
<span className={`mob-nav-icon bi ${item.icon}`}></span>
{item.label}
</NavLink>
))}
</div>
)
})}
</nav>
</>
)
}