import { useState } from 'react'; import { Plus, Server, MapPin, Zap, HardDrive, Wrench, ChevronDown, ChevronUp, Thermometer, Shield, X } from 'lucide-react'; import { useGameStore } from '@/store'; import { formatMoney, formatNumber, formatPercent, LOCATION_CONFIGS, DC_TIER_CONFIGS, RACK_SKU_CONFIGS, } from '@ai-tycoon/shared'; import type { DCTier, RackSkuId, LocationId, RackOrder, PipelineStage, Era } from '@ai-tycoon/shared'; const ERA_ORDER: Era[] = ['startup', 'scaleup', 'bigtech', 'agi']; const STAGE_LABELS: Record = { ordered: 'Ordered', manufacturing: 'Manufacturing', receiving: 'Receiving', installation: 'Installation', testing: 'Testing', repair: 'Repair', decommission: 'Decom', }; const STAGE_COLORS: Record = { ordered: 'bg-surface-600', manufacturing: 'bg-blue-500', receiving: 'bg-cyan-500', installation: 'bg-violet-500', testing: 'bg-amber-500', repair: 'bg-danger', decommission: 'bg-surface-500', }; function PipelineKanban() { const pipeline = useGameStore((s) => s.infrastructure.rackPipeline); if (pipeline.length === 0) return null; const stages: PipelineStage[] = ['ordered', 'manufacturing', 'receiving', 'installation', 'testing', 'repair', 'decommission']; const grouped = stages.map(stage => ({ stage, orders: pipeline.filter(o => o.stage === stage), })); return (

Rack Pipeline

{grouped.map(({ stage, orders }) => (
{STAGE_LABELS[stage]} ({orders.length})
{orders.map(order => ( ))}
))}
); } function PipelineCard({ order }: { order: RackOrder }) { const sku = RACK_SKU_CONFIGS[order.skuId]; const progress = order.stageTotal > 0 ? order.stageProgress / order.stageTotal : 0; return (
{sku.name}
{order.repairCount > 0 && (
{order.repairCount}x
)}
); } function CapacityBar({ label, used, max, unit, icon: Icon }: { label: string; used: number; max: number; unit: string; icon: typeof HardDrive; }) { const pct = max > 0 ? used / max : 0; const color = pct > 0.9 ? 'bg-danger' : pct > 0.7 ? 'bg-amber-500' : 'bg-accent'; return (
{label} {formatNumber(used)}/{formatNumber(max)} {unit}
); } function DataCenterCard({ dcId }: { dcId: string }) { const dc = useGameStore((s) => s.infrastructure.dataCenters.find(d => d.id === dcId))!; const pipelineForDc = useGameStore((s) => s.infrastructure.rackPipeline.filter(o => o.dataCenterId === dcId)); const money = useGameStore((s) => s.economy.money); const era = useGameStore((s) => s.meta.currentEra); const completedResearch = useGameStore((s) => s.research.completedResearch); const orderRack = useGameStore((s) => s.orderRack); const decommissionRack = useGameStore((s) => s.decommissionRack); const upgradeDataCenter = useGameStore((s) => s.upgradeDataCenter); const [expanded, setExpanded] = useState(true); const tierConfig = DC_TIER_CONFIGS[dc.tier]; const currentEraIdx = ERA_ORDER.indexOf(era); const activePipeline = pipelineForDc.filter(o => o.stage !== 'decommission'); const liveUsedSlots = dc.racks.length + activePipeline.length; const liveUsedPower = dc.racks.reduce((s, r) => s + RACK_SKU_CONFIGS[r.skuId].powerDrawKW, 0) + activePipeline.reduce((s, o) => s + RACK_SKU_CONFIGS[o.skuId].powerDrawKW, 0); const availableSkus = Object.values(RACK_SKU_CONFIGS).filter(sku => { if (ERA_ORDER.indexOf(sku.era) > currentEraIdx) return false; if (sku.requiredResearch && !completedResearch.includes(sku.requiredResearch)) return false; return true; }); if (dc.status === 'constructing') { const pct = dc.constructionTotal > 0 ? dc.constructionProgress / dc.constructionTotal : 0; return (

{dc.name}

Under Construction — {tierConfig.name}
{LOCATION_CONFIGS[dc.location].name}
{Math.round(pct * 100)}% — {dc.constructionTotal - dc.constructionProgress}s remaining
); } return (

{dc.name}

{dc.tier}
{LOCATION_CONFIGS[dc.location].name} Uptime: {formatPercent(dc.currentUptime)} Cost: {formatMoney(dc.energyCostPerTick + dc.maintenanceCostPerTick)}/s
{expanded && ( <> {dc.racks.length > 0 && (

Production Racks ({dc.racks.length})

{dc.racks.map(rack => { const sku = RACK_SKU_CONFIGS[rack.skuId]; return (
{sku.name}
{formatNumber(sku.flopsPerRack)} FLOPS
); })}
)}

Order Racks

{availableSkus.map(sku => { const canAfford = money >= sku.baseCost; const hasSlot = liveUsedSlots < tierConfig.rackSlots; const hasPower = liveUsedPower + sku.powerDrawKW <= tierConfig.powerBudgetKW; const disabled = !canAfford || !hasSlot || !hasPower; return ( ); })}

Upgrades

)}
); } function BuildDCPanel({ onClose }: { onClose: () => void }) { const money = useGameStore((s) => s.economy.money); const era = useGameStore((s) => s.meta.currentEra); const completedResearch = useGameStore((s) => s.research.completedResearch); const buildDataCenter = useGameStore((s) => s.buildDataCenter); const [name, setName] = useState(''); const [location, setLocation] = useState('us-west'); const [tier, setTier] = useState('small'); const currentEraIdx = ERA_ORDER.indexOf(era); const availableLocations = Object.values(LOCATION_CONFIGS).filter( loc => ERA_ORDER.indexOf(loc.availableAt) <= currentEraIdx, ); const availableTiers = Object.values(DC_TIER_CONFIGS).filter(t => { if (ERA_ORDER.indexOf(t.requiredEra) > currentEraIdx) return false; if (t.requiredResearch && !completedResearch.includes(t.requiredResearch)) return false; return true; }); const tierConfig = DC_TIER_CONFIGS[tier]; const handleBuild = () => { if (!name.trim()) return; buildDataCenter(name.trim(), location, tier); onClose(); }; return (

Build New Data Center

setName(e.target.value)} placeholder="DC-West-01" className="w-full bg-surface-800 border border-surface-600 rounded px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-accent/50" autoFocus />
Cost: {formatMoney(tierConfig.baseCost)} · Build time: {tierConfig.buildTimeTicks}s
); } export function InfrastructurePage() { const dataCenters = useGameStore((s) => s.infrastructure.dataCenters); const [showNewDC, setShowNewDC] = useState(false); return (

Infrastructure

{showNewDC && setShowNewDC(false)} />} {dataCenters.length === 0 && !showNewDC ? (

No data centers yet. Build your first one to start hosting AI models.

) : (
{dataCenters.map(dc => ( ))}
)}
); }