mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-03 08:53:36 +00:00
Implement initial project structure and features for Automaker application, including environment setup, auto mode services, and session management. Update port configurations to 3007 and add new UI components for enhanced user interaction.
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useAppStore } from "@/store/app-store";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
FolderOpen,
|
||||
Plus,
|
||||
@@ -16,11 +16,34 @@ import {
|
||||
ChevronRight,
|
||||
Folder,
|
||||
X,
|
||||
Moon,
|
||||
Sun,
|
||||
Search,
|
||||
Wrench,
|
||||
PanelLeft,
|
||||
PanelLeftClose,
|
||||
Sparkles,
|
||||
User,
|
||||
LogOut,
|
||||
Cpu,
|
||||
ChevronDown,
|
||||
Check,
|
||||
} from "lucide-react";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
|
||||
interface NavSection {
|
||||
label?: string;
|
||||
items: NavItem[];
|
||||
}
|
||||
|
||||
interface NavItem {
|
||||
id: string;
|
||||
label: string;
|
||||
icon: any;
|
||||
}
|
||||
|
||||
export function Sidebar() {
|
||||
const {
|
||||
@@ -28,192 +51,358 @@ export function Sidebar() {
|
||||
currentProject,
|
||||
currentView,
|
||||
sidebarOpen,
|
||||
theme,
|
||||
setCurrentProject,
|
||||
setCurrentView,
|
||||
toggleSidebar,
|
||||
removeProject,
|
||||
setTheme,
|
||||
} = useAppStore();
|
||||
|
||||
const [hoveredProject, setHoveredProject] = useState<string | null>(null);
|
||||
const [userMenuOpen, setUserMenuOpen] = useState(false);
|
||||
const userMenuRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const navItems = [
|
||||
{ id: "spec" as const, label: "Spec Editor", icon: FileText },
|
||||
{ id: "board" as const, label: "Kanban Board", icon: LayoutGrid },
|
||||
{ id: "code" as const, label: "Code View", icon: Code },
|
||||
{ id: "analysis" as const, label: "Analysis", icon: Search },
|
||||
{ id: "agent" as const, label: "Agent Chat", icon: Bot },
|
||||
{ id: "tools" as const, label: "Agent Tools", icon: Wrench },
|
||||
// Close dropdown when clicking outside
|
||||
useEffect(() => {
|
||||
function handleClickOutside(event: MouseEvent) {
|
||||
if (
|
||||
userMenuRef.current &&
|
||||
!userMenuRef.current.contains(event.target as Node)
|
||||
) {
|
||||
setUserMenuOpen(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (userMenuOpen) {
|
||||
document.addEventListener("mousedown", handleClickOutside);
|
||||
return () => {
|
||||
document.removeEventListener("mousedown", handleClickOutside);
|
||||
};
|
||||
}
|
||||
}, [userMenuOpen]);
|
||||
|
||||
const navSections: NavSection[] = [
|
||||
{
|
||||
label: "Project",
|
||||
items: [
|
||||
{ id: "board", label: "Kanban Board", icon: LayoutGrid },
|
||||
{ id: "agent", label: "Agent Runner", icon: Bot },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Tools",
|
||||
items: [
|
||||
{ id: "spec", label: "Spec Editor", icon: FileText },
|
||||
{ id: "code", label: "Code View", icon: Code },
|
||||
{ id: "analysis", label: "Analysis", icon: Search },
|
||||
{ id: "tools", label: "Agent Tools", icon: Wrench },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const toggleTheme = () => {
|
||||
setTheme(theme === "dark" ? "light" : "dark");
|
||||
const isActiveRoute = (id: string) => {
|
||||
return currentView === id;
|
||||
};
|
||||
|
||||
return (
|
||||
<aside
|
||||
className={cn(
|
||||
"flex flex-col h-full bg-sidebar border-r border-sidebar-border transition-all duration-300",
|
||||
sidebarOpen ? "w-64" : "w-16"
|
||||
"flex-shrink-0 border-r border-white/10 bg-zinc-950/50 backdrop-blur-md flex flex-col z-30 transition-all duration-300 relative",
|
||||
sidebarOpen ? "w-16 lg:w-60" : "w-16"
|
||||
)}
|
||||
data-testid="sidebar"
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between h-14 px-4 border-b border-sidebar-border titlebar-drag-region">
|
||||
{sidebarOpen && (
|
||||
<h1 className="text-lg font-bold text-sidebar-foreground">
|
||||
Automaker
|
||||
</h1>
|
||||
{/* Floating Collapse Toggle Button - Desktop only */}
|
||||
<button
|
||||
onClick={toggleSidebar}
|
||||
className="hidden lg:flex absolute top-20 -right-3 z-50 items-center justify-center w-6 h-6 rounded-full bg-zinc-800 border border-white/10 text-zinc-400 hover:text-white hover:bg-zinc-700 hover:border-white/20 transition-all shadow-lg titlebar-no-drag"
|
||||
data-testid="sidebar-collapse-button"
|
||||
title={sidebarOpen ? "Collapse sidebar" : "Expand sidebar"}
|
||||
>
|
||||
{sidebarOpen ? (
|
||||
<PanelLeftClose className="w-3.5 h-3.5" />
|
||||
) : (
|
||||
<PanelLeft className="w-3.5 h-3.5" />
|
||||
)}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={toggleSidebar}
|
||||
className="titlebar-no-drag text-sidebar-foreground hover:bg-sidebar-accent"
|
||||
data-testid="toggle-sidebar"
|
||||
>
|
||||
{sidebarOpen ? (
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
) : (
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{/* Project Actions */}
|
||||
<div className="p-2 space-y-1">
|
||||
<Button
|
||||
variant="ghost"
|
||||
<div className="flex-1 flex flex-col overflow-hidden">
|
||||
{/* Logo */}
|
||||
<div
|
||||
className={cn(
|
||||
"w-full justify-start gap-3 text-sidebar-foreground hover:bg-sidebar-accent",
|
||||
!sidebarOpen && "justify-center px-2"
|
||||
"h-20 pt-8 flex items-center justify-between border-b border-zinc-800 flex-shrink-0 titlebar-drag-region",
|
||||
sidebarOpen ? "px-3 lg:px-6" : "px-3"
|
||||
)}
|
||||
onClick={() => setCurrentView("welcome")}
|
||||
data-testid="new-project-button"
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
{sidebarOpen && <span>New Project</span>}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className={cn(
|
||||
"w-full justify-start gap-3 text-sidebar-foreground hover:bg-sidebar-accent",
|
||||
!sidebarOpen && "justify-center px-2"
|
||||
)}
|
||||
onClick={() => setCurrentView("welcome")}
|
||||
data-testid="open-project-button"
|
||||
>
|
||||
<FolderOpen className="h-4 w-4" />
|
||||
{sidebarOpen && <span>Open Project</span>}
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
className="flex items-center titlebar-no-drag cursor-pointer"
|
||||
onClick={() => setCurrentView("welcome")}
|
||||
data-testid="logo-button"
|
||||
>
|
||||
<div className="relative flex items-center justify-center w-8 h-8 bg-gradient-to-br from-brand-500 to-purple-600 rounded-lg shadow-lg shadow-brand-500/20 group">
|
||||
<Cpu className="text-white w-5 h-5 group-hover:rotate-12 transition-transform" />
|
||||
</div>
|
||||
<span
|
||||
className={cn(
|
||||
"ml-3 font-bold text-white text-base tracking-tight",
|
||||
sidebarOpen ? "hidden lg:block" : "hidden"
|
||||
)}
|
||||
>
|
||||
Auto<span className="text-brand-500">maker</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Projects List */}
|
||||
{sidebarOpen && projects.length > 0 && (
|
||||
<div className="flex-1 overflow-y-auto px-2">
|
||||
<p className="px-2 py-2 text-xs font-medium text-muted-foreground uppercase tracking-wider">
|
||||
Recent Projects
|
||||
</p>
|
||||
<div className="space-y-1">
|
||||
{projects.map((project) => (
|
||||
<div
|
||||
key={project.id}
|
||||
className={cn(
|
||||
"group flex items-center gap-2 px-2 py-2 rounded-md cursor-pointer transition-colors",
|
||||
currentProject?.id === project.id
|
||||
? "bg-sidebar-accent text-sidebar-accent-foreground"
|
||||
: "hover:bg-sidebar-accent/50 text-sidebar-foreground"
|
||||
)}
|
||||
onClick={() => setCurrentProject(project)}
|
||||
onMouseEnter={() => setHoveredProject(project.id)}
|
||||
onMouseLeave={() => setHoveredProject(null)}
|
||||
data-testid={`project-${project.id}`}
|
||||
>
|
||||
<Folder className="h-4 w-4 shrink-0" />
|
||||
<span className="flex-1 truncate text-sm">{project.name}</span>
|
||||
{hoveredProject === project.id && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-6 w-6 opacity-0 group-hover:opacity-100"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
removeProject(project.id);
|
||||
}}
|
||||
data-testid={`remove-project-${project.id}`}
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{/* Project Actions */}
|
||||
<div className="flex items-center gap-1 titlebar-no-drag">
|
||||
<button
|
||||
onClick={() => setCurrentView("welcome")}
|
||||
className="group flex items-center justify-center w-8 h-8 rounded-lg relative overflow-hidden transition-all text-zinc-400 hover:text-white hover:bg-white/5"
|
||||
title="New Project"
|
||||
data-testid="new-project-button"
|
||||
>
|
||||
<Plus className="w-4 h-4 flex-shrink-0" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setCurrentView("welcome")}
|
||||
className="group flex items-center justify-center w-8 h-8 rounded-lg relative overflow-hidden transition-all text-zinc-400 hover:text-white hover:bg-white/5"
|
||||
title="Open Project"
|
||||
data-testid="open-project-button"
|
||||
>
|
||||
<FolderOpen className="w-4 h-4 flex-shrink-0" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Navigation - Only show when a project is open */}
|
||||
{currentProject && (
|
||||
<div className="border-t border-sidebar-border p-2 space-y-1">
|
||||
{sidebarOpen && (
|
||||
<p className="px-2 py-2 text-xs font-medium text-muted-foreground uppercase tracking-wider">
|
||||
Views
|
||||
</p>
|
||||
)}
|
||||
{navItems.map((item) => (
|
||||
<Button
|
||||
key={item.id}
|
||||
variant="ghost"
|
||||
className={cn(
|
||||
"w-full justify-start gap-3",
|
||||
!sidebarOpen && "justify-center px-2",
|
||||
currentView === item.id
|
||||
? "bg-sidebar-accent text-sidebar-accent-foreground"
|
||||
: "text-sidebar-foreground hover:bg-sidebar-accent/50"
|
||||
{/* Project Selector */}
|
||||
{sidebarOpen && projects.length > 0 && (
|
||||
<div className="px-2 mt-3">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button
|
||||
className="w-full flex items-center justify-between px-3 py-2.5 rounded-lg bg-white/5 border border-white/10 hover:bg-white/10 transition-all text-white titlebar-no-drag"
|
||||
data-testid="project-selector"
|
||||
>
|
||||
<div className="flex items-center gap-2 flex-1 min-w-0">
|
||||
<Folder className="h-4 w-4 text-brand-500 flex-shrink-0" />
|
||||
<span className="text-sm font-medium truncate">
|
||||
{currentProject?.name || "Select Project"}
|
||||
</span>
|
||||
</div>
|
||||
<ChevronDown className="h-4 w-4 text-zinc-400 flex-shrink-0" />
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
className="w-56 bg-zinc-800 border-zinc-700"
|
||||
align="start"
|
||||
>
|
||||
{projects.map((project) => (
|
||||
<DropdownMenuItem
|
||||
key={project.id}
|
||||
onClick={() => setCurrentProject(project)}
|
||||
className="flex items-center gap-2 cursor-pointer text-zinc-300 hover:text-white hover:bg-zinc-700/50"
|
||||
data-testid={`project-option-${project.id}`}
|
||||
>
|
||||
<Folder className="h-4 w-4" />
|
||||
<span className="flex-1 truncate">{project.name}</span>
|
||||
{currentProject?.id === project.id && (
|
||||
<Check className="h-4 w-4 text-brand-500" />
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{/* Nav Items - Scrollable */}
|
||||
<nav className="flex-1 overflow-y-auto px-2 mt-4 pb-2">
|
||||
{navSections.map((section, sectionIdx) => (
|
||||
<div key={sectionIdx} className={sectionIdx > 0 ? "mt-6" : ""}>
|
||||
{/* Section Label */}
|
||||
{section.label && sidebarOpen && (
|
||||
<div className="hidden lg:block px-4 mb-2">
|
||||
<span className="text-[10px] font-semibold text-zinc-500 uppercase tracking-wider">
|
||||
{section.label}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{section.label && !sidebarOpen && (
|
||||
<div className="h-px bg-zinc-800 mx-2 mb-2"></div>
|
||||
)}
|
||||
onClick={() => setCurrentView(item.id)}
|
||||
data-testid={`nav-${item.id}`}
|
||||
>
|
||||
<item.icon className="h-4 w-4" />
|
||||
{sidebarOpen && <span>{item.label}</span>}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Bottom Actions */}
|
||||
<div className="mt-auto border-t border-sidebar-border p-2 space-y-1">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className={cn(
|
||||
"w-full justify-start gap-3 text-sidebar-foreground hover:bg-sidebar-accent",
|
||||
!sidebarOpen && "justify-center px-2"
|
||||
)}
|
||||
onClick={toggleTheme}
|
||||
data-testid="toggle-theme"
|
||||
>
|
||||
{theme === "dark" ? (
|
||||
<Sun className="h-4 w-4" />
|
||||
) : (
|
||||
<Moon className="h-4 w-4" />
|
||||
)}
|
||||
{sidebarOpen && (
|
||||
<span>{theme === "dark" ? "Light Mode" : "Dark Mode"}</span>
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className={cn(
|
||||
"w-full justify-start gap-3 text-sidebar-foreground hover:bg-sidebar-accent",
|
||||
!sidebarOpen && "justify-center px-2",
|
||||
currentView === "settings" && "bg-sidebar-accent text-sidebar-accent-foreground"
|
||||
)}
|
||||
onClick={() => setCurrentView("settings")}
|
||||
data-testid="settings-button"
|
||||
>
|
||||
<Settings className="h-4 w-4" />
|
||||
{sidebarOpen && <span>Settings</span>}
|
||||
</Button>
|
||||
{/* Nav Items */}
|
||||
<div className="space-y-1">
|
||||
{section.items.map((item) => {
|
||||
const isActive = isActiveRoute(item.id);
|
||||
const Icon = item.icon;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={item.id}
|
||||
onClick={() => setCurrentView(item.id as any)}
|
||||
className={cn(
|
||||
"group flex items-center w-full px-2 lg:px-3 py-2.5 rounded-lg relative overflow-hidden transition-all titlebar-no-drag",
|
||||
isActive
|
||||
? "bg-white/5 text-white border border-white/10"
|
||||
: "text-zinc-400 hover:text-white hover:bg-white/5"
|
||||
)}
|
||||
title={!sidebarOpen ? item.label : undefined}
|
||||
data-testid={`nav-${item.id}`}
|
||||
>
|
||||
{isActive && (
|
||||
<div className="absolute inset-y-0 left-0 w-0.5 bg-brand-500 rounded-l-md"></div>
|
||||
)}
|
||||
<Icon
|
||||
className={cn(
|
||||
"w-4 h-4 flex-shrink-0 transition-colors",
|
||||
isActive
|
||||
? "text-brand-500"
|
||||
: "group-hover:text-brand-400"
|
||||
)}
|
||||
/>
|
||||
<span
|
||||
className={cn(
|
||||
"ml-2.5 font-medium text-sm",
|
||||
sidebarOpen ? "hidden lg:block" : "hidden"
|
||||
)}
|
||||
>
|
||||
{item.label}
|
||||
</span>
|
||||
{/* Tooltip for collapsed state */}
|
||||
{!sidebarOpen && (
|
||||
<span
|
||||
className="absolute left-full ml-2 px-2 py-1 bg-zinc-800 text-white text-xs rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap z-50 border border-zinc-700"
|
||||
data-testid={`sidebar-tooltip-${item.label.toLowerCase()}`}
|
||||
>
|
||||
{item.label}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{/* Bottom Section - User / Settings */}
|
||||
<div className="border-t border-zinc-800 bg-zinc-900/50 flex-shrink-0">
|
||||
{/* Settings Link */}
|
||||
<div className="p-2">
|
||||
<button
|
||||
onClick={() => setCurrentView("settings")}
|
||||
className={cn(
|
||||
"group flex items-center w-full px-2 lg:px-3 py-2.5 rounded-lg relative overflow-hidden transition-all titlebar-no-drag",
|
||||
isActiveRoute("settings")
|
||||
? "bg-white/5 text-white border border-white/10"
|
||||
: "text-zinc-400 hover:text-white hover:bg-white/5",
|
||||
sidebarOpen ? "justify-start" : "justify-center"
|
||||
)}
|
||||
title={!sidebarOpen ? "Settings" : undefined}
|
||||
data-testid="settings-button"
|
||||
>
|
||||
{isActiveRoute("settings") && (
|
||||
<div className="absolute inset-y-0 left-0 w-0.5 bg-brand-500 rounded-l-md"></div>
|
||||
)}
|
||||
<Settings
|
||||
className={cn(
|
||||
"w-4 h-4 flex-shrink-0 transition-colors",
|
||||
isActiveRoute("settings")
|
||||
? "text-brand-500"
|
||||
: "group-hover:text-brand-400"
|
||||
)}
|
||||
/>
|
||||
<span
|
||||
className={cn(
|
||||
"ml-2.5 font-medium text-sm",
|
||||
sidebarOpen ? "hidden lg:block" : "hidden"
|
||||
)}
|
||||
>
|
||||
Settings
|
||||
</span>
|
||||
{!sidebarOpen && (
|
||||
<span className="absolute left-full ml-2 px-2 py-1 bg-zinc-800 text-white text-xs rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap z-50 border border-zinc-700">
|
||||
Settings
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* User Profile */}
|
||||
<div className="p-3 border-t border-zinc-800" ref={userMenuRef}>
|
||||
<div className="relative">
|
||||
<button
|
||||
onClick={() => setUserMenuOpen(!userMenuOpen)}
|
||||
className={cn(
|
||||
"flex items-center p-1.5 rounded-lg transition-colors group relative w-full hover:bg-white/5 titlebar-no-drag",
|
||||
sidebarOpen ? "lg:space-x-2.5" : "justify-center"
|
||||
)}
|
||||
>
|
||||
<div className="relative">
|
||||
<div className="w-8 h-8 rounded-full border border-zinc-600 bg-gradient-to-br from-brand-500 to-purple-600 flex items-center justify-center">
|
||||
<User className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<div className="absolute bottom-0 right-0 w-2 h-2 bg-green-500 border-2 border-zinc-900 rounded-full"></div>
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
"overflow-hidden",
|
||||
sidebarOpen ? "hidden lg:block" : "hidden"
|
||||
)}
|
||||
>
|
||||
<p className="text-xs font-medium text-white truncate">
|
||||
Developer
|
||||
</p>
|
||||
<p className="text-[10px] text-zinc-500 truncate">
|
||||
Active Session
|
||||
</p>
|
||||
</div>
|
||||
{/* Tooltip for user when collapsed */}
|
||||
{!sidebarOpen && (
|
||||
<span
|
||||
className="absolute left-full ml-2 px-2 py-1 bg-zinc-800 text-white text-xs rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap z-50 border border-zinc-700"
|
||||
data-testid="sidebar-tooltip-user"
|
||||
>
|
||||
Developer
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* Dropdown Menu */}
|
||||
{userMenuOpen && (
|
||||
<div
|
||||
className={cn(
|
||||
"absolute bottom-full mb-2 bg-zinc-800 border border-zinc-700 rounded-xl shadow-lg overflow-hidden z-50",
|
||||
sidebarOpen ? "left-0 right-0" : "left-0"
|
||||
)}
|
||||
>
|
||||
<div className="py-2">
|
||||
<button
|
||||
onClick={() => {
|
||||
setUserMenuOpen(false);
|
||||
setCurrentView("settings");
|
||||
}}
|
||||
className="flex items-center w-full px-4 py-2 text-sm text-zinc-300 hover:bg-zinc-700/50 hover:text-white transition-colors titlebar-no-drag"
|
||||
>
|
||||
<Settings className="w-4 h-4 mr-3" />
|
||||
<span>Settings</span>
|
||||
</button>
|
||||
<div className="border-t border-zinc-700 my-2"></div>
|
||||
<button
|
||||
onClick={() => {
|
||||
setUserMenuOpen(false);
|
||||
// Add logout logic here if needed
|
||||
}}
|
||||
className="flex items-center w-full px-4 py-2 text-sm text-red-400 hover:bg-zinc-700/50 hover:text-red-300 transition-colors titlebar-no-drag"
|
||||
>
|
||||
<LogOut className="w-4 h-4 mr-3" />
|
||||
<span>Exit</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user