mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-11-01 18:19:43 +00:00
Update
This commit is contained in:
1 parent
75509b0e45
commit
ef7bc8c90d
16 files changed
+886
-32
No files matched your search
@@ -13,11 +13,13 @@ import {
|
||||
MessageSquareText,
|
||||
Target,
|
||||
Bot,
|
||||
Smartphone,
|
||||
X,
|
||||
} from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import logoImage from '@/assets/logo.png';
|
||||
|
||||
interface NavItem {
|
||||
@@ -70,6 +72,12 @@ const systemNav: NavItem[] = [
|
||||
icon: <Bot className="h-5 w-5" />,
|
||||
requiredPermission: PERMISSION_KEYS.MANAGE_AI_CONFIG,
|
||||
},
|
||||
{
|
||||
label: 'App Settings',
|
||||
href: '/app-settings',
|
||||
icon: <Smartphone className="h-5 w-5" />,
|
||||
requiredPermission: PERMISSION_KEYS.MANAGE_APP_CONFIG,
|
||||
},
|
||||
{
|
||||
label: 'Redemption Codes',
|
||||
href: '/redemption-codes',
|
||||
@@ -84,11 +92,23 @@ const systemNav: NavItem[] = [
|
||||
},
|
||||
];
|
||||
|
||||
export function Sidebar() {
|
||||
interface SidebarProps {
|
||||
mobileOpen?: boolean;
|
||||
onMobileClose?: () => void;
|
||||
}
|
||||
|
||||
export function Sidebar({ mobileOpen, onMobileClose }: SidebarProps) {
|
||||
const location = useLocation();
|
||||
const { user, permissions, logout, hasPermission } = useAuth();
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
|
||||
// Close mobile sidebar on route change
|
||||
useEffect(() => {
|
||||
if (mobileOpen && onMobileClose) {
|
||||
onMobileClose();
|
||||
}
|
||||
}, [location.pathname]);
|
||||
|
||||
const NavLink = ({ item }: { item: NavItem }) => {
|
||||
if (item.requiredPermission && !hasPermission(item.requiredPermission)) return null;
|
||||
|
||||
@@ -102,7 +122,7 @@ export function Sidebar() {
|
||||
isActive
|
||||
? "bg-gradient-rose text-white shadow-lg glow-rose-sm"
|
||||
: "text-muted-foreground hover:bg-white/5 hover:text-foreground",
|
||||
collapsed && "justify-center px-2"
|
||||
collapsed && "justify-center px-2 lg:justify-center lg:px-2"
|
||||
)}
|
||||
>
|
||||
<span className={cn(
|
||||
@@ -134,11 +154,13 @@ export function Sidebar() {
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
const sidebarContent = (
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-screen flex-col border-r border-white/5 bg-gradient-to-b from-card to-background transition-all duration-300",
|
||||
collapsed ? "w-20" : "w-72"
|
||||
// Desktop only: hidden on mobile/tablet, show on desktop
|
||||
"hidden xl:flex",
|
||||
collapsed ? "xl:w-20" : "xl:w-72"
|
||||
)}
|
||||
>
|
||||
{/* Logo */}
|
||||
@@ -236,4 +258,101 @@ export function Sidebar() {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Mobile sidebar (overlay)
|
||||
const mobileSidebar = (
|
||||
<>
|
||||
{/* Backdrop */}
|
||||
<div
|
||||
className={cn(
|
||||
"fixed inset-0 z-40 bg-black/60 backdrop-blur-sm transition-opacity xl:hidden",
|
||||
mobileOpen ? "opacity-100" : "opacity-0 pointer-events-none"
|
||||
)}
|
||||
onClick={onMobileClose}
|
||||
/>
|
||||
|
||||
{/* Slide-in sidebar */}
|
||||
<div
|
||||
className={cn(
|
||||
"fixed inset-y-0 left-0 z-50 w-72 transform transition-transform duration-300 ease-in-out xl:hidden",
|
||||
mobileOpen ? "translate-x-0" : "-translate-x-full"
|
||||
)}
|
||||
>
|
||||
<div className="flex h-full flex-col border-r border-white/5 bg-gradient-to-b from-card to-background">
|
||||
{/* Mobile header with close button */}
|
||||
<div className="flex h-20 items-center justify-between px-4 border-b border-white/5">
|
||||
<Link to="/" className="flex items-center gap-3 group">
|
||||
<div className="relative">
|
||||
<img
|
||||
src={logoImage}
|
||||
alt="Sauci"
|
||||
className="h-10 w-10 object-contain"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-xl font-bold tracking-tight text-gradient-sunset">
|
||||
Sauci
|
||||
</span>
|
||||
<span className="text-[10px] font-medium uppercase tracking-widest text-muted-foreground">
|
||||
Admin Portal
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={onMobileClose}
|
||||
className="h-10 w-10 rounded-lg text-muted-foreground hover:text-foreground hover:bg-white/5"
|
||||
>
|
||||
<X className="h-5 w-5" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
<div className="flex-1 space-y-6 overflow-auto px-3 py-4">
|
||||
<NavSection title="Content" items={contentNav} />
|
||||
<NavSection title="Users" items={usersNav} />
|
||||
<NavSection title="System" items={systemNav} />
|
||||
</div>
|
||||
|
||||
<Separator className="bg-white/5" />
|
||||
|
||||
{/* User info */}
|
||||
<div className="p-4">
|
||||
<div className="flex items-center gap-3 rounded-xl p-2 transition-colors hover:bg-white/5">
|
||||
<Avatar className="h-9 w-9 ring-2 ring-primary/20">
|
||||
<AvatarFallback className="bg-gradient-rose text-white font-semibold">
|
||||
{user?.email?.charAt(0).toUpperCase() || 'A'}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<p className="truncate text-sm font-medium text-foreground">
|
||||
{user?.email}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground capitalize">
|
||||
{permissions?.role?.replace('_', ' ')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="mt-2 w-full justify-start text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors"
|
||||
onClick={logout}
|
||||
>
|
||||
<LogOut className="h-4 w-4" />
|
||||
<span className="ml-2">Sign Out</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{sidebarContent}
|
||||
{mobileSidebar}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user