=== NEW === - studio/ — MCPEngine Studio scaffold (Next.js monorepo, build plan) - docs/FACTORY-V2.md — Factory v2 architecture doc - docs/CALENDLY_MCP_BUILD_SUMMARY.md — Calendly MCP build report === UPDATED SERVERS === - fieldedge: Added jobs-tools, UI build script, main entry update - lightspeed: Updated main + server entry points - squarespace: Added collection-browser + page-manager apps - toast: Added main + server entry points === INFRA === - infra/command-center/state.json — Updated pipeline state - infra/command-center/FACTORY-V2.md — Factory v2 operator playbook
109 lines
3.4 KiB
TypeScript
109 lines
3.4 KiB
TypeScript
'use client';
|
|
|
|
import React from 'react';
|
|
import Link from 'next/link';
|
|
import { usePathname } from 'next/navigation';
|
|
import {
|
|
LayoutDashboard,
|
|
Workflow,
|
|
Palette,
|
|
FlaskConical,
|
|
Rocket,
|
|
Store,
|
|
type LucideIcon,
|
|
} from 'lucide-react';
|
|
|
|
interface NavItem {
|
|
icon: LucideIcon;
|
|
label: string;
|
|
href: string;
|
|
segment: string;
|
|
}
|
|
|
|
const NAV_ITEMS: NavItem[] = [
|
|
{ icon: LayoutDashboard, label: 'Dashboard', href: '/', segment: '' },
|
|
{ icon: Workflow, label: 'Editor', href: '/editor', segment: 'editor' },
|
|
{ icon: Palette, label: 'Apps', href: '/apps', segment: 'apps' },
|
|
{ icon: FlaskConical, label: 'Tests', href: '/tests', segment: 'tests' },
|
|
{ icon: Rocket, label: 'Deploy', href: '/deploy', segment: 'deploy' },
|
|
{ icon: Store, label: 'Marketplace', href: '/marketplace', segment: 'marketplace' },
|
|
];
|
|
|
|
function NavRail() {
|
|
const pathname = usePathname();
|
|
|
|
// Extract project id from path: /(dashboard)/projects/[id]/...
|
|
const segments = pathname.split('/');
|
|
const projectIdIndex = segments.indexOf('projects') + 1;
|
|
const projectId = segments[projectIdIndex] ?? '';
|
|
const currentSegment = segments[projectIdIndex + 1] ?? '';
|
|
|
|
return (
|
|
<nav className="flex flex-col items-center w-16 py-4 bg-gray-900 border-r border-gray-800 shrink-0">
|
|
{/* Logo / Home */}
|
|
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center mb-6">
|
|
<span className="text-white text-xs font-bold">M</span>
|
|
</div>
|
|
|
|
{/* Nav Items */}
|
|
<div className="flex flex-col items-center gap-1 flex-1">
|
|
{NAV_ITEMS.map((item) => {
|
|
// For dashboard, match exact (empty segment). For others, match segment.
|
|
const isActive = item.segment === ''
|
|
? currentSegment === '' || currentSegment === undefined
|
|
: currentSegment === item.segment;
|
|
|
|
const href = item.segment === ''
|
|
? `/projects/${projectId}`
|
|
: `/projects/${projectId}/${item.segment}`;
|
|
|
|
const Icon = item.icon;
|
|
|
|
return (
|
|
<Link
|
|
key={item.segment}
|
|
href={href}
|
|
title={item.label}
|
|
className={`
|
|
flex items-center justify-center w-10 h-10 rounded-lg
|
|
transition-colors duration-150 relative group
|
|
${isActive
|
|
? 'bg-indigo-500/15 text-indigo-400'
|
|
: 'text-gray-500 hover:text-gray-300 hover:bg-gray-800/60'
|
|
}
|
|
`}
|
|
>
|
|
<Icon className="w-5 h-5" />
|
|
|
|
{/* Active indicator */}
|
|
{isActive && (
|
|
<span className="absolute left-0 top-1/2 -translate-y-1/2 w-0.5 h-5 bg-indigo-500 rounded-r" />
|
|
)}
|
|
|
|
{/* Tooltip */}
|
|
<span className="absolute left-14 bg-gray-800 text-gray-200 text-xs px-2 py-1 rounded shadow-lg opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none whitespace-nowrap z-50">
|
|
{item.label}
|
|
</span>
|
|
</Link>
|
|
);
|
|
})}
|
|
</div>
|
|
</nav>
|
|
);
|
|
}
|
|
|
|
interface DashboardLayoutProps {
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
export default function DashboardLayout({ children }: DashboardLayoutProps) {
|
|
return (
|
|
<div className="flex h-screen bg-[#0A0F1E] text-gray-100">
|
|
<NavRail />
|
|
<main className="flex-1 flex flex-col overflow-hidden">
|
|
{children}
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|