09a5cb69a7
CI / build-and-push (push) Successful in 42s
Replaces the simplified single-subscriber market with a full competitive simulation: shared TAM with softmax market shares across 4 segments, multi-tier consumer subscriptions (Free/Plus/Pro/Team) and API tiers (Free/PAYG/Scale/Enterprise), enterprise sales pipeline (Lead→Qualification→POC→Negotiation→Active→Renewal) with SLA tracking, developer ecosystem flywheel, technology obsolescence pressure, seasonal demand cycles, and two new product lines (Code Assistant, AI Agents Platform). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
65 lines
3.2 KiB
TypeScript
65 lines
3.2 KiB
TypeScript
import { useGameStore } from '@/store';
|
|
import { formatMoney, formatNumber, formatPercent } from '@ai-tycoon/shared';
|
|
import { Share2, Copy, Check } from 'lucide-react';
|
|
import { useState } from 'react';
|
|
import { ACHIEVEMENT_DEFINITIONS } from '@ai-tycoon/game-engine';
|
|
|
|
export function CompanyStatsCard({ onClose }: { onClose: () => void }) {
|
|
const [copied, setCopied] = useState(false);
|
|
const companyName = useGameStore((s) => s.meta.companyName);
|
|
const era = useGameStore((s) => s.meta.currentEra);
|
|
const totalPlayTime = useGameStore((s) => s.meta.totalPlayTime);
|
|
const money = useGameStore((s) => s.economy.money);
|
|
const totalRevenue = useGameStore((s) => s.economy.totalRevenue);
|
|
const valuation = useGameStore((s) => s.economy.funding.valuation);
|
|
const subscribers = useGameStore((s) => s.market.consumerTiers.totalUsers);
|
|
const models = useGameStore((s) => s.models.baseModels.length);
|
|
const bestModel = useGameStore((s) => s.models.bestDeployedModelScore);
|
|
const reputation = useGameStore((s) => s.reputation.score);
|
|
const achievements = useGameStore((s) => s.achievements.unlocked.length);
|
|
const dataCenters = useGameStore((s) => s.infrastructure.totalDataCenterCount);
|
|
const totalRacks = useGameStore((s) => s.infrastructure.totalRackCount);
|
|
|
|
const eraLabel = era === 'startup' ? 'Startup' : era === 'scaleup' ? 'Scale-up' : era === 'bigtech' ? 'Big Tech' : 'AGI';
|
|
const hours = Math.floor(totalPlayTime / 3600);
|
|
const minutes = Math.floor((totalPlayTime % 3600) / 60);
|
|
|
|
const statsText = [
|
|
`${companyName} — AI Tycoon`,
|
|
`Era: ${eraLabel} | Playtime: ${hours}h ${minutes}m`,
|
|
`Cash: ${formatMoney(money)} | Revenue: ${formatMoney(totalRevenue)}`,
|
|
`Valuation: ${formatMoney(valuation)}`,
|
|
`Subscribers: ${formatNumber(subscribers)} | Models: ${models}`,
|
|
`Best Model: ${bestModel.toFixed(1)}/100 | Reputation: ${reputation}/100`,
|
|
`Data Centers: ${dataCenters} | Racks: ${totalRacks} | Achievements: ${achievements}/${ACHIEVEMENT_DEFINITIONS.length}`,
|
|
].join('\n');
|
|
|
|
const handleCopy = () => {
|
|
navigator.clipboard.writeText(statsText);
|
|
setCopied(true);
|
|
setTimeout(() => setCopied(false), 2000);
|
|
};
|
|
|
|
return (
|
|
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50" onClick={onClose}>
|
|
<div className="bg-surface-900 border border-surface-700 rounded-xl p-6 max-w-md w-full mx-4 shadow-2xl" onClick={e => e.stopPropagation()}>
|
|
<div className="flex items-center justify-between mb-4">
|
|
<h3 className="text-lg font-bold flex items-center gap-2"><Share2 size={18} /> Company Stats</h3>
|
|
<button onClick={onClose} className="text-surface-400 hover:text-surface-200 text-sm">Close</button>
|
|
</div>
|
|
|
|
<div className="bg-surface-800 rounded-lg p-4 font-mono text-xs leading-relaxed whitespace-pre-line text-surface-200">
|
|
{statsText}
|
|
</div>
|
|
|
|
<button
|
|
onClick={handleCopy}
|
|
className="mt-4 w-full flex items-center justify-center gap-2 bg-accent hover:bg-accent-dark text-white rounded-lg py-2 text-sm font-medium"
|
|
>
|
|
{copied ? <><Check size={14} /> Copied!</> : <><Copy size={14} /> Copy to Clipboard</>}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|