Jake Shore 96e52666c5 MCPEngine full sync — studio scaffold, factory v2, server updates, state.json — 2026-02-12
=== 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
2026-02-12 17:58:33 -05:00

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>
);
}