working on storage dashboard
This commit is contained in:
@@ -1,11 +1,23 @@
|
||||
import { Outlet, Link, useNavigate } from 'react-router-dom'
|
||||
import { Outlet, Link, useNavigate, useLocation } from 'react-router-dom'
|
||||
import { useAuthStore } from '@/store/auth'
|
||||
import { LogOut, Menu } from 'lucide-react'
|
||||
import {
|
||||
LogOut,
|
||||
Menu,
|
||||
LayoutDashboard,
|
||||
HardDrive,
|
||||
Database,
|
||||
Network,
|
||||
Settings,
|
||||
Bell,
|
||||
Server,
|
||||
Users
|
||||
} from 'lucide-react'
|
||||
import { useState } from 'react'
|
||||
|
||||
export default function Layout() {
|
||||
const { user, clearAuth } = useAuthStore()
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const [sidebarOpen, setSidebarOpen] = useState(true)
|
||||
|
||||
const handleLogout = () => {
|
||||
@@ -14,84 +26,100 @@ export default function Layout() {
|
||||
}
|
||||
|
||||
const navigation = [
|
||||
{ name: 'Dashboard', href: '/', icon: '📊' },
|
||||
{ name: 'Storage', href: '/storage', icon: '💾' },
|
||||
{ name: 'Tape Libraries', href: '/tape', icon: '📼' },
|
||||
{ name: 'iSCSI Targets', href: '/iscsi', icon: '🔌' },
|
||||
{ name: 'Tasks', href: '/tasks', icon: '⚙️' },
|
||||
{ name: 'Alerts', href: '/alerts', icon: '🔔' },
|
||||
{ name: 'System', href: '/system', icon: '🖥️' },
|
||||
{ name: 'Dashboard', href: '/', icon: LayoutDashboard },
|
||||
{ name: 'Storage', href: '/storage', icon: HardDrive },
|
||||
{ name: 'Tape Libraries', href: '/tape', icon: Database },
|
||||
{ name: 'iSCSI Targets', href: '/iscsi', icon: Network },
|
||||
{ name: 'Tasks', href: '/tasks', icon: Settings },
|
||||
{ name: 'Alerts', href: '/alerts', icon: Bell },
|
||||
{ name: 'System', href: '/system', icon: Server },
|
||||
]
|
||||
|
||||
if (user?.roles.includes('admin')) {
|
||||
navigation.push({ name: 'IAM', href: '/iam', icon: '👥' })
|
||||
navigation.push({ name: 'IAM', href: '/iam', icon: Users })
|
||||
}
|
||||
|
||||
const isActive = (href: string) => {
|
||||
if (href === '/') {
|
||||
return location.pathname === '/'
|
||||
}
|
||||
return location.pathname.startsWith(href)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="min-h-screen bg-background-dark">
|
||||
{/* Sidebar */}
|
||||
<div
|
||||
className={`fixed inset-y-0 left-0 z-50 w-64 bg-gray-900 text-white transition-transform duration-300 ${
|
||||
className={`fixed inset-y-0 left-0 z-50 w-64 bg-background-dark border-r border-border-dark text-white transition-transform duration-300 ${
|
||||
sidebarOpen ? 'translate-x-0' : '-translate-x-full'
|
||||
}`}
|
||||
>
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="flex items-center justify-between p-4 border-b border-gray-800">
|
||||
<h1 className="text-xl font-bold">Calypso</h1>
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between px-6 py-5 border-b border-border-dark">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-8 h-8 bg-primary rounded-lg flex items-center justify-center">
|
||||
<span className="text-white font-bold text-sm">C</span>
|
||||
</div>
|
||||
<h1 className="text-xl font-black text-white font-display tracking-tight">Calypso</h1>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setSidebarOpen(false)}
|
||||
className="lg:hidden text-gray-400 hover:text-white"
|
||||
className="lg:hidden text-text-secondary hover:text-white transition-colors"
|
||||
>
|
||||
<Menu className="h-6 w-6" />
|
||||
<Menu className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
<nav className="flex-1 p-4 space-y-2">
|
||||
{navigation.map((item) => (
|
||||
<Link
|
||||
key={item.name}
|
||||
to={item.href}
|
||||
className="flex items-center space-x-3 px-4 py-2 rounded-lg hover:bg-gray-800 transition-colors"
|
||||
>
|
||||
<span>{item.icon}</span>
|
||||
<span>{item.name}</span>
|
||||
</Link>
|
||||
))}
|
||||
|
||||
{/* Navigation */}
|
||||
<nav className="flex-1 px-3 py-4 space-y-1 overflow-y-auto custom-scrollbar">
|
||||
{navigation.map((item) => {
|
||||
const Icon = item.icon
|
||||
const active = isActive(item.href)
|
||||
return (
|
||||
<Link
|
||||
key={item.name}
|
||||
to={item.href}
|
||||
className={`flex items-center gap-3 px-4 py-2.5 rounded-lg transition-all ${
|
||||
active
|
||||
? 'bg-primary/20 text-primary border-l-2 border-primary'
|
||||
: 'text-text-secondary hover:bg-card-dark hover:text-white'
|
||||
}`}
|
||||
>
|
||||
<Icon className={`h-5 w-5 ${active ? 'text-primary' : ''}`} />
|
||||
<span className={`text-sm font-medium ${active ? 'font-semibold' : ''}`}>
|
||||
{item.name}
|
||||
</span>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</nav>
|
||||
<div className="p-4 border-t border-gray-800">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
<p className="text-sm font-medium">{user?.username}</p>
|
||||
<p className="text-xs text-gray-400">{user?.roles.join(', ')}</p>
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="p-4 border-t border-border-dark bg-[#0d1419]">
|
||||
<div className="mb-3 px-2">
|
||||
<p className="text-sm font-semibold text-white mb-0.5">{user?.username}</p>
|
||||
<p className="text-xs text-text-secondary font-mono">
|
||||
{user?.roles.join(', ').toUpperCase()}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="w-full flex items-center space-x-2 px-4 py-2 rounded-lg hover:bg-gray-800 transition-colors"
|
||||
className="w-full flex items-center gap-2 px-4 py-2.5 rounded-lg text-text-secondary hover:bg-card-dark hover:text-white transition-colors border border-border-dark"
|
||||
>
|
||||
<LogOut className="h-4 w-4" />
|
||||
<span>Logout</span>
|
||||
<span className="text-sm font-medium">Logout</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main content */}
|
||||
<div className={`transition-all duration-300 ${sidebarOpen ? 'lg:ml-64' : 'ml-0'}`}>
|
||||
{/* Top bar */}
|
||||
<div className="bg-white shadow-sm border-b border-gray-200">
|
||||
<div className="flex items-center justify-between px-6 py-4">
|
||||
<button
|
||||
onClick={() => setSidebarOpen(!sidebarOpen)}
|
||||
className="lg:hidden text-gray-600 hover:text-gray-900"
|
||||
>
|
||||
<Menu className="h-6 w-6" />
|
||||
</button>
|
||||
<div className="flex-1" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`transition-all duration-300 ${sidebarOpen ? 'lg:ml-64' : 'ml-0'} bg-background-dark`}>
|
||||
{/* Top bar - removed for dashboard design */}
|
||||
|
||||
{/* Page content */}
|
||||
<main className="p-6">
|
||||
<main className="min-h-screen">
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user