VIBN Frontend for Coolify deployment
This commit is contained in:
655
components/layout/project-sidebar.tsx
Normal file
655
components/layout/project-sidebar.tsx
Normal file
@@ -0,0 +1,655 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useCallback, useEffect, useRef } from "react";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
LayoutDashboard,
|
||||
Target,
|
||||
ListChecks,
|
||||
Palette,
|
||||
Code2,
|
||||
Server,
|
||||
Zap,
|
||||
ChevronDown,
|
||||
ChevronRight,
|
||||
Github,
|
||||
MessageSquare,
|
||||
Image,
|
||||
Globe,
|
||||
FolderOpen,
|
||||
Inbox,
|
||||
Users,
|
||||
Eye,
|
||||
Plus,
|
||||
} from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { MCPConnectModal } from "./mcp-connect-modal";
|
||||
import { ConnectSourcesModal } from "./connect-sources-modal";
|
||||
import { OpenAIIcon, V0Icon, CursorIcon } from "@/components/icons/custom-icons";
|
||||
|
||||
interface ProjectSidebarProps {
|
||||
projectId: string;
|
||||
activeSection?: string; // From left rail: 'projects', 'inbox', 'clients', etc.
|
||||
workspace?: string;
|
||||
}
|
||||
|
||||
// Map section IDs to display names
|
||||
const SECTION_NAMES: Record<string, string> = {
|
||||
home: 'Home',
|
||||
product: 'Product',
|
||||
site: 'Site',
|
||||
pricing: 'Pricing',
|
||||
content: 'Content',
|
||||
social: 'Social',
|
||||
inbox: 'Inbox',
|
||||
people: 'People',
|
||||
settings: 'Settings',
|
||||
};
|
||||
|
||||
// Section-specific navigation items
|
||||
const SECTION_ITEMS: Record<string, Array<{title: string; icon: any; href: string}>> = {
|
||||
home: [
|
||||
// { title: "Vision", icon: Eye, href: "/vision" }, // Hidden per user request
|
||||
{ title: "Context", icon: FolderOpen, href: "/context" },
|
||||
],
|
||||
product: [
|
||||
{ title: "Product Vision", icon: Target, href: "/plan" },
|
||||
{ title: "Progress", icon: ListChecks, href: "/progress" },
|
||||
{ title: "UI UX", icon: Palette, href: "/design" },
|
||||
{ title: "Code", icon: Code2, href: "/code" },
|
||||
{ title: "Deployment", icon: Server, href: "/deployment" },
|
||||
{ title: "Automation", icon: Zap, href: "/automation" },
|
||||
],
|
||||
site: [
|
||||
{ title: "Pages", icon: Globe, href: "/site/pages" },
|
||||
{ title: "Templates", icon: Palette, href: "/site/templates" },
|
||||
{ title: "Settings", icon: Target, href: "/site/settings" },
|
||||
],
|
||||
pricing: [
|
||||
{ title: "Plans", icon: Target, href: "/pricing/plans" },
|
||||
{ title: "Billing", icon: Code2, href: "/pricing/billing" },
|
||||
{ title: "Invoices", icon: ListChecks, href: "/pricing/invoices" },
|
||||
],
|
||||
content: [
|
||||
{ title: "Blog Posts", icon: Target, href: "/content/blog" },
|
||||
{ title: "Case Studies", icon: Code2, href: "/content/cases" },
|
||||
{ title: "Documentation", icon: ListChecks, href: "/content/docs" },
|
||||
],
|
||||
social: [
|
||||
{ title: "Posts", icon: MessageSquare, href: "/social/posts" },
|
||||
{ title: "Analytics", icon: Target, href: "/social/analytics" },
|
||||
{ title: "Schedule", icon: ListChecks, href: "/social/schedule" },
|
||||
],
|
||||
inbox: [
|
||||
{ title: "All", icon: Inbox, href: "/inbox/all" },
|
||||
{ title: "Unread", icon: Target, href: "/inbox/unread" },
|
||||
{ title: "Archived", icon: ListChecks, href: "/inbox/archived" },
|
||||
],
|
||||
people: [
|
||||
{ title: "Team", icon: Users, href: "/people/team" },
|
||||
{ title: "Clients", icon: Users, href: "/people/clients" },
|
||||
{ title: "Contacts", icon: Users, href: "/people/contacts" },
|
||||
],
|
||||
};
|
||||
|
||||
type ConnectionStatus = 'inactive' | 'connected' | 'live';
|
||||
|
||||
export function ProjectSidebar({ projectId, activeSection = 'projects', workspace = 'marks-account' }: ProjectSidebarProps) {
|
||||
const minWidth = 200;
|
||||
const maxWidth = 500;
|
||||
const [width, setWidth] = useState(minWidth);
|
||||
const [isResizing, setIsResizing] = useState(false);
|
||||
const [mcpModalOpen, setMcpModalOpen] = useState(false);
|
||||
const [connectModalOpen, setConnectModalOpen] = useState(false);
|
||||
const [isUserFlowsExpanded, setIsUserFlowsExpanded] = useState(true);
|
||||
const [isProductScreensExpanded, setIsProductScreensExpanded] = useState(true);
|
||||
const [getStartedCompleted, setGetStartedCompleted] = useState(false);
|
||||
const pathname = usePathname();
|
||||
const sidebarRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Connection states - mock data, would come from API/database in production
|
||||
const [connectionStates, setConnectionStates] = useState<{
|
||||
github: ConnectionStatus;
|
||||
openai: ConnectionStatus;
|
||||
v0: ConnectionStatus;
|
||||
cursor: ConnectionStatus;
|
||||
}>({
|
||||
github: 'connected',
|
||||
openai: 'live',
|
||||
v0: 'inactive',
|
||||
cursor: 'connected',
|
||||
});
|
||||
|
||||
// Helper function to get icon classes based on connection status
|
||||
const getIconClasses = (status: ConnectionStatus) => {
|
||||
switch (status) {
|
||||
case 'inactive':
|
||||
return 'text-muted-foreground/40';
|
||||
case 'connected':
|
||||
return 'text-muted-foreground';
|
||||
case 'live':
|
||||
return 'text-foreground';
|
||||
default:
|
||||
return 'text-muted-foreground/40';
|
||||
}
|
||||
};
|
||||
|
||||
const startResizing = useCallback((e: React.MouseEvent) => {
|
||||
setIsResizing(true);
|
||||
e.preventDefault();
|
||||
}, []);
|
||||
|
||||
const stopResizing = useCallback(() => {
|
||||
setIsResizing(false);
|
||||
}, []);
|
||||
|
||||
const resize = useCallback(
|
||||
(e: MouseEvent) => {
|
||||
if (isResizing) {
|
||||
const newWidth = e.clientX - 64; // Subtract left rail width (64px)
|
||||
if (newWidth >= minWidth && newWidth <= maxWidth) {
|
||||
setWidth(newWidth);
|
||||
}
|
||||
}
|
||||
},
|
||||
[isResizing]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("mousemove", resize);
|
||||
window.addEventListener("mouseup", stopResizing);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("mousemove", resize);
|
||||
window.removeEventListener("mouseup", stopResizing);
|
||||
};
|
||||
}, [resize, stopResizing]);
|
||||
|
||||
// Determine header title based on active section
|
||||
const isVAIPage = pathname?.includes('/v_ai_chat');
|
||||
const headerTitle = isVAIPage ? 'v_ai' : (SECTION_NAMES[activeSection] || 'Home');
|
||||
|
||||
// Get section-specific items
|
||||
const currentSectionItems = SECTION_ITEMS[activeSection] || SECTION_ITEMS['home'];
|
||||
|
||||
return (
|
||||
<>
|
||||
<aside
|
||||
ref={sidebarRef}
|
||||
style={{ width: `${width}px` }}
|
||||
className="relative flex flex-col border-r bg-card/50"
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="flex h-14 items-center justify-between px-4 border-b">
|
||||
<h2 className="font-semibold text-sm">{headerTitle}</h2>
|
||||
{/* Connection icons - only show for Product section */}
|
||||
{activeSection === 'product' && (
|
||||
<div className="flex items-center gap-1">
|
||||
{/* GitHub */}
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className="h-7 w-7 relative"
|
||||
onClick={() => setConnectModalOpen(true)}
|
||||
>
|
||||
<Github className={cn("h-4 w-4", getIconClasses(connectionStates.github))} />
|
||||
{connectionStates.github === 'live' && (
|
||||
<span className="absolute top-1 right-1 h-1.5 w-1.5 rounded-full bg-green-500 animate-pulse" />
|
||||
)}
|
||||
</Button>
|
||||
|
||||
{/* OpenAI/ChatGPT */}
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className="h-7 w-7 relative"
|
||||
onClick={() => setConnectModalOpen(true)}
|
||||
>
|
||||
<OpenAIIcon className={cn("h-4 w-4", getIconClasses(connectionStates.openai))} />
|
||||
{connectionStates.openai === 'live' && (
|
||||
<span className="absolute top-1 right-1 h-1.5 w-1.5 rounded-full bg-green-500 animate-pulse" />
|
||||
)}
|
||||
</Button>
|
||||
|
||||
{/* v0 */}
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className="h-7 w-7 relative"
|
||||
onClick={() => setConnectModalOpen(true)}
|
||||
>
|
||||
<V0Icon className={cn("h-4 w-4", getIconClasses(connectionStates.v0))} />
|
||||
{connectionStates.v0 === 'live' && (
|
||||
<span className="absolute top-1 right-1 h-1.5 w-1.5 rounded-full bg-green-500 animate-pulse" />
|
||||
)}
|
||||
</Button>
|
||||
|
||||
{/* Cursor */}
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className="h-7 w-7 relative"
|
||||
onClick={() => setConnectModalOpen(true)}
|
||||
>
|
||||
<CursorIcon className={cn("h-4 w-4", getIconClasses(connectionStates.cursor))} />
|
||||
{connectionStates.cursor === 'live' && (
|
||||
<span className="absolute top-1 right-1 h-1.5 w-1.5 rounded-full bg-green-500 animate-pulse" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Section-Specific Navigation */}
|
||||
<div className="px-2 py-1.5 space-y-0.5">
|
||||
{/* v_ai - Persistent AI Chat - Always show for Home section */}
|
||||
{activeSection === 'home' && (
|
||||
<div className="mb-2">
|
||||
<Link
|
||||
href={`/${workspace}/project/${projectId}/v_ai_chat`}
|
||||
className={cn(
|
||||
"flex items-center gap-2 rounded-md px-2 py-1 text-sm transition-colors",
|
||||
pathname === `/${workspace}/project/${projectId}/v_ai_chat`
|
||||
? "bg-accent text-accent-foreground font-medium"
|
||||
: "text-muted-foreground hover:bg-accent hover:text-accent-foreground"
|
||||
)}
|
||||
>
|
||||
<img src="/vibn-logo-circle.png" alt="v_ai" className="h-4 w-4 shrink-0" />
|
||||
<span className="truncate font-medium">v_ai</span>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{currentSectionItems.map((item) => {
|
||||
const href = `/${workspace}/project/${projectId}${item.href}`;
|
||||
const isActive = pathname === href;
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={href}
|
||||
className={cn(
|
||||
"flex items-center gap-2 rounded-md px-2 py-1 text-sm transition-colors",
|
||||
isActive
|
||||
? "bg-accent text-accent-foreground font-medium"
|
||||
: "text-muted-foreground hover:bg-accent hover:text-accent-foreground"
|
||||
)}
|
||||
>
|
||||
<item.icon className="h-4 w-4 shrink-0" />
|
||||
<span className="truncate">{item.title}</span>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Divider */}
|
||||
<Separator className="my-1.5" />
|
||||
|
||||
{/* Context Section - Only show in Home section */}
|
||||
{activeSection === 'home' && !isVAIPage && (
|
||||
<div className="flex-1 overflow-hidden flex flex-col">
|
||||
<div className="px-2 py-2">
|
||||
<div className="flex items-center justify-between px-2 mb-2">
|
||||
<h3 className="text-xs font-semibold text-muted-foreground uppercase tracking-wider">Context</h3>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="h-6 px-2 text-xs text-primary hover:text-primary hover:bg-primary/10"
|
||||
onClick={() => {
|
||||
// Navigate to context page
|
||||
window.location.href = `/${workspace}/project/${projectId}/context`;
|
||||
}}
|
||||
>
|
||||
<Plus className="h-3 w-3 mr-1" />
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Context Section - Shows items based on selected project section */}
|
||||
{!isVAIPage && activeSection !== 'home' && (
|
||||
<ScrollArea className="flex-1 px-2">
|
||||
<div className="space-y-1 py-1.5">
|
||||
{/* Show context based on current page */}
|
||||
{pathname.includes('/plan') && (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between px-2">
|
||||
<h3 className="text-xs font-semibold text-muted-foreground">VISION SOURCES</h3>
|
||||
<button
|
||||
onClick={() => setMcpModalOpen(true)}
|
||||
className="flex items-center gap-1 rounded px-1.5 py-0.5 text-xs hover:bg-accent transition-colors text-muted-foreground hover:text-foreground"
|
||||
>
|
||||
{/* OpenAI Icon (SVG) */}
|
||||
<svg
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className="opacity-60"
|
||||
>
|
||||
<path
|
||||
d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<span>Connect</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors text-muted-foreground">
|
||||
<span className="text-xs">📄</span>
|
||||
<span className="truncate">chatgpt-brainstorm.json</span>
|
||||
</button>
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors text-muted-foreground">
|
||||
<span className="text-xs">📄</span>
|
||||
<span className="truncate">product-notes.md</span>
|
||||
</button>
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm border border-dashed border-primary/30 text-primary hover:bg-primary/5 transition-colors">
|
||||
<span className="text-xs">+</span>
|
||||
<span className="truncate">Upload new file</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{pathname.includes('/progress') && (
|
||||
<div className="space-y-2">
|
||||
<h3 className="px-2 text-xs font-semibold text-muted-foreground">FILTERS</h3>
|
||||
<div className="space-y-1">
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors">
|
||||
<span>All Tasks</span>
|
||||
</button>
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors text-muted-foreground">
|
||||
<span>In Progress</span>
|
||||
</button>
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors text-muted-foreground">
|
||||
<span>Completed</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{pathname.includes('/design') && (
|
||||
<div className="space-y-1.5">
|
||||
{/* Sandbox Indicator */}
|
||||
<div className="px-2 py-1">
|
||||
<h3 className="text-xs font-semibold text-muted-foreground">YOUR SANDBOX</h3>
|
||||
</div>
|
||||
|
||||
{/* User Flows */}
|
||||
<button
|
||||
onClick={() => setIsUserFlowsExpanded(!isUserFlowsExpanded)}
|
||||
className="flex w-full items-center justify-between px-2 py-1 rounded hover:bg-accent transition-colors"
|
||||
>
|
||||
<h3 className="text-xs font-semibold text-muted-foreground">USER FLOWS</h3>
|
||||
{isUserFlowsExpanded ? (
|
||||
<ChevronDown className="h-3 w-3 text-muted-foreground" />
|
||||
) : (
|
||||
<ChevronRight className="h-3 w-3 text-muted-foreground" />
|
||||
)}
|
||||
</button>
|
||||
{isUserFlowsExpanded && (
|
||||
<div className="space-y-0.5">
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1 text-sm hover:bg-accent transition-colors text-muted-foreground">
|
||||
<span>🚀 Onboarding</span>
|
||||
</button>
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1 text-sm hover:bg-accent transition-colors text-muted-foreground">
|
||||
<span>✍️ Signup</span>
|
||||
</button>
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1 text-sm hover:bg-accent transition-colors text-muted-foreground">
|
||||
<span>👋 New User</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Product Screens */}
|
||||
<button
|
||||
onClick={() => setIsProductScreensExpanded(!isProductScreensExpanded)}
|
||||
className="flex w-full items-center justify-between px-2 py-1 rounded hover:bg-accent transition-colors mt-2.5"
|
||||
>
|
||||
<h3 className="text-xs font-semibold text-muted-foreground">PRODUCT SCREENS</h3>
|
||||
{isProductScreensExpanded ? (
|
||||
<ChevronDown className="h-3 w-3 text-muted-foreground" />
|
||||
) : (
|
||||
<ChevronRight className="h-3 w-3 text-muted-foreground" />
|
||||
)}
|
||||
</button>
|
||||
{isProductScreensExpanded && (
|
||||
<div className="space-y-0.5">
|
||||
<Link
|
||||
href={`/${projectId}/design/landing-hero`}
|
||||
className={cn(
|
||||
"group flex w-full items-center justify-between rounded-md px-2 py-1 text-sm transition-colors",
|
||||
pathname === `/${projectId}/design/landing-hero`
|
||||
? "bg-accent text-accent-foreground font-medium"
|
||||
: "text-muted-foreground hover:bg-accent hover:text-accent-foreground"
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<span>✨</span>
|
||||
<span>Landing Hero</span>
|
||||
</div>
|
||||
<span className="text-xs opacity-0 group-hover:opacity-100 transition-opacity">3</span>
|
||||
</Link>
|
||||
<Link
|
||||
href={`/${projectId}/design/dashboard`}
|
||||
className={cn(
|
||||
"group flex w-full items-center justify-between rounded-md px-2 py-1 text-sm transition-colors",
|
||||
pathname === `/${projectId}/design/dashboard`
|
||||
? "bg-accent text-accent-foreground font-medium"
|
||||
: "text-muted-foreground hover:bg-accent hover:text-accent-foreground"
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<span>📊</span>
|
||||
<span>Dashboard</span>
|
||||
</div>
|
||||
<span className="text-xs opacity-0 group-hover:opacity-100 transition-opacity">1</span>
|
||||
</Link>
|
||||
<Link
|
||||
href={`/${projectId}/design/pricing`}
|
||||
className={cn(
|
||||
"group flex w-full items-center justify-between rounded-md px-2 py-1 text-sm transition-colors",
|
||||
pathname === `/${projectId}/design/pricing`
|
||||
? "bg-accent text-accent-foreground font-medium"
|
||||
: "text-muted-foreground hover:bg-accent hover:text-accent-foreground"
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<span>💳</span>
|
||||
<span>Pricing</span>
|
||||
</div>
|
||||
<span className="text-xs opacity-0 group-hover:opacity-100 transition-opacity">2</span>
|
||||
</Link>
|
||||
<Link
|
||||
href={`/${projectId}/design/user-profile`}
|
||||
className={cn(
|
||||
"group flex w-full items-center justify-between rounded-md px-2 py-1 text-sm transition-colors",
|
||||
pathname === `/${projectId}/design/user-profile`
|
||||
? "bg-accent text-accent-foreground font-medium"
|
||||
: "text-muted-foreground hover:bg-accent hover:text-accent-foreground"
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<span>👤</span>
|
||||
<span>User Profile</span>
|
||||
</div>
|
||||
<span className="text-xs opacity-0 group-hover:opacity-100 transition-opacity">1</span>
|
||||
</Link>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="px-2 pt-1.5">
|
||||
<button className="flex w-full items-center justify-center gap-2 rounded-md border border-dashed border-primary/30 px-2 py-1.5 text-sm text-primary hover:bg-primary/5 transition-colors">
|
||||
<span>+</span>
|
||||
<span>New Screen</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{pathname.includes('/code') && (
|
||||
<div className="space-y-2">
|
||||
<h3 className="px-2 text-xs font-semibold text-muted-foreground">QUICK LINKS</h3>
|
||||
<div className="space-y-1">
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors text-muted-foreground">
|
||||
<span>📦 Repository</span>
|
||||
</button>
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors text-muted-foreground">
|
||||
<span>🌿 Branches</span>
|
||||
</button>
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors text-muted-foreground">
|
||||
<span>🔀 Pull Requests</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{pathname.includes('/deployment') && (
|
||||
<div className="space-y-2">
|
||||
<h3 className="px-2 text-xs font-semibold text-muted-foreground">ENVIRONMENTS</h3>
|
||||
<div className="space-y-1">
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors text-muted-foreground">
|
||||
<span>🟢 Production</span>
|
||||
</button>
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors text-muted-foreground">
|
||||
<span>🟡 Staging</span>
|
||||
</button>
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors text-muted-foreground">
|
||||
<span>🔵 Development</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{pathname.includes('/automation') && (
|
||||
<div className="space-y-2">
|
||||
<h3 className="px-2 text-xs font-semibold text-muted-foreground">WORKFLOWS</h3>
|
||||
<div className="space-y-1">
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors text-muted-foreground">
|
||||
<span>⚡ Active Jobs</span>
|
||||
</button>
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors text-muted-foreground">
|
||||
<span>⏰ Scheduled</span>
|
||||
</button>
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors text-muted-foreground">
|
||||
<span>📋 Logs</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Default: Left rail context sections */}
|
||||
{activeSection === 'inbox' && (
|
||||
<div className="space-y-2">
|
||||
<h3 className="px-2 text-xs font-semibold text-muted-foreground">INBOX</h3>
|
||||
<div className="space-y-1">
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors">
|
||||
<span className="text-muted-foreground">No new items</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{activeSection === 'clients' && (
|
||||
<div className="space-y-2">
|
||||
<h3 className="px-2 text-xs font-semibold text-muted-foreground">PEOPLE</h3>
|
||||
<div className="space-y-1">
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors">
|
||||
<span>Personal Projects</span>
|
||||
</button>
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors">
|
||||
<span>VIBN</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{activeSection === 'invoices' && (
|
||||
<div className="space-y-2">
|
||||
<h3 className="px-2 text-xs font-semibold text-muted-foreground">GROW</h3>
|
||||
<div className="space-y-1">
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors">
|
||||
<span>No growth strategies yet</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{activeSection === 'site' && (
|
||||
<div className="space-y-2">
|
||||
<h3 className="px-2 text-xs font-semibold text-muted-foreground">SITE</h3>
|
||||
<div className="space-y-1">
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors">
|
||||
<span>Pages</span>
|
||||
</button>
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors">
|
||||
<span>Settings</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{activeSection === 'content' && (
|
||||
<div className="space-y-2">
|
||||
<h3 className="px-2 text-xs font-semibold text-muted-foreground">CONTENT</h3>
|
||||
<div className="space-y-1">
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors">
|
||||
<span>Blog Posts</span>
|
||||
</button>
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors">
|
||||
<span>Case Studies</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{activeSection === 'social' && (
|
||||
<div className="space-y-2">
|
||||
<h3 className="px-2 text-xs font-semibold text-muted-foreground">SOCIAL</h3>
|
||||
<div className="space-y-1">
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors">
|
||||
<span>Posts</span>
|
||||
</button>
|
||||
<button className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent transition-colors">
|
||||
<span>Analytics</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
)}
|
||||
|
||||
{/* Footer */}
|
||||
<div className="flex h-10 items-center justify-between border-t px-4 text-xs text-muted-foreground">
|
||||
<span>v1.0.0</span>
|
||||
<button className="hover:text-foreground transition-colors">
|
||||
Help
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Resize Handle */}
|
||||
<div
|
||||
onMouseDown={startResizing}
|
||||
className="absolute right-0 top-0 h-full w-1 cursor-col-resize hover:bg-primary/20 active:bg-primary/40 transition-colors"
|
||||
/>
|
||||
</aside>
|
||||
|
||||
<MCPConnectModal
|
||||
open={mcpModalOpen}
|
||||
onOpenChange={setMcpModalOpen}
|
||||
projectId={projectId}
|
||||
/>
|
||||
|
||||
<ConnectSourcesModal
|
||||
open={connectModalOpen}
|
||||
onOpenChange={setConnectModalOpen}
|
||||
projectId={projectId}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user