Initial scaffold: AI Tycoon monorepo with core game loop
Turborepo monorepo with three packages: - packages/shared: TypeScript types for all 14 game systems + balance constants + formatting utils - packages/game-engine: Pure TS simulation engine with tick processor, economy, infrastructure, compute, research, market, and reputation systems - apps/web: React + Vite + Tailwind + Zustand frontend with sidebar dashboard layout, new game screen, dashboard with charts, infrastructure management, and model training pages Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import {
|
||||
LayoutDashboard, Server, FlaskConical, Brain,
|
||||
TrendingUp, Users, Database, Swords, DollarSign, Settings,
|
||||
} from 'lucide-react';
|
||||
import { useGameStore, type ActivePage } from '@/store';
|
||||
|
||||
const NAV_ITEMS: { page: ActivePage; label: string; icon: typeof LayoutDashboard; era?: string }[] = [
|
||||
{ page: 'dashboard', label: 'Dashboard', icon: LayoutDashboard },
|
||||
{ page: 'infrastructure', label: 'Infrastructure', icon: Server },
|
||||
{ page: 'research', label: 'Research', icon: FlaskConical },
|
||||
{ page: 'models', label: 'Models', icon: Brain },
|
||||
{ page: 'market', label: 'Market', icon: TrendingUp },
|
||||
{ page: 'finance', label: 'Finance', icon: DollarSign },
|
||||
{ page: 'talent', label: 'Talent', icon: Users, era: 'scaleup' },
|
||||
{ page: 'data', label: 'Data', icon: Database, era: 'scaleup' },
|
||||
{ page: 'competitors', label: 'Competitors', icon: Swords, era: 'scaleup' },
|
||||
{ page: 'settings', label: 'Settings', icon: Settings },
|
||||
];
|
||||
|
||||
export function Sidebar() {
|
||||
const activePage = useGameStore((s) => s.activePage);
|
||||
const setActivePage = useGameStore((s) => s.setActivePage);
|
||||
const companyName = useGameStore((s) => s.meta.companyName);
|
||||
const era = useGameStore((s) => s.meta.currentEra);
|
||||
|
||||
const eraOrder = ['startup', 'scaleup', 'bigtech', 'agi'];
|
||||
const currentEraIdx = eraOrder.indexOf(era);
|
||||
|
||||
return (
|
||||
<aside className="w-56 bg-surface-900 border-r border-surface-700 flex flex-col h-screen">
|
||||
<div className="p-4 border-b border-surface-700">
|
||||
<h1 className="text-lg font-bold text-accent-light truncate">{companyName}</h1>
|
||||
<span className="text-xs text-surface-400 uppercase tracking-wider">
|
||||
{era === 'startup' ? 'Startup' : era === 'scaleup' ? 'Scale-up' : era === 'bigtech' ? 'Big Tech' : 'AGI Era'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<nav className="flex-1 py-2 overflow-y-auto">
|
||||
{NAV_ITEMS.map(({ page, label, icon: Icon, era: requiredEra }) => {
|
||||
if (requiredEra && eraOrder.indexOf(requiredEra) > currentEraIdx) return null;
|
||||
|
||||
const isActive = activePage === page;
|
||||
return (
|
||||
<button
|
||||
key={page}
|
||||
onClick={() => setActivePage(page)}
|
||||
className={`w-full flex items-center gap-3 px-4 py-2.5 text-sm transition-colors ${
|
||||
isActive
|
||||
? 'bg-accent/10 text-accent-light border-r-2 border-accent'
|
||||
: 'text-surface-300 hover:bg-surface-800 hover:text-surface-100'
|
||||
}`}
|
||||
>
|
||||
<Icon size={18} />
|
||||
{label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
|
||||
<div className="p-4 border-t border-surface-700 text-xs text-surface-500">
|
||||
AI Tycoon v0.1
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user