import React, { useState, useEffect } from 'react';
import { Calculator, TrendingUp, AlertCircle, Coins, Award, HelpCircle } from 'lucide-react';
export default function App() {
// 基礎設定狀態
const [cardName, setCardName] = useState('比卡超 Vmax (Pikachu Vmax)');
const [rawCost, setRawCost] = useState(150); // 裸卡成本預設
const [gradingFee, setGradingFee] = useState(150); // 評級費預設
// 價值預期狀態
const [value10, setValue10] = useState(2000); // 10分價值
const [value9, setValue9] = useState(600); // 9分價值
const [valueOther, setValueOther] = useState(150); // 8分或以下價值(通常等於裸卡或略低)
// 機率狀態 (%)
const [prob10, setProb10] = useState(40);
const [prob9, setProb9] = useState(50);
const probOther = 100 - prob10 - prob9;
// 處理機率變動,確保總和不超過 100%
const handleProb10Change = (e) => {
let val = parseInt(e.target.value) || 0;
if (val < 0) val = 0;
if (val > 100) val = 100;
setProb10(val);
if (val + prob9 > 100) {
setProb9(100 - val);
}
};
const handleProb9Change = (e) => {
let val = parseInt(e.target.value) || 0;
if (val < 0) val = 0;
if (val > 100) val = 100;
setProb9(val);
if (val + prob10 > 100) {
setProb10(100 - val);
}
};
// 計算結果
const totalCost = parseFloat(rawCost) + parseFloat(gradingFee);
const profit10 = value10 - totalCost;
const roi10 = totalCost > 0 ? ((profit10 / totalCost) * 100).toFixed(1) : 0;
const profit9 = value9 - totalCost;
const roi9 = totalCost > 0 ? ((profit9 / totalCost) * 100).toFixed(1) : 0;
const profitOther = valueOther - totalCost;
// 期望值 (Expected Value) 計算
const expectedRevenue = (value10 * (prob10 / 100)) + (value9 * (prob9 / 100)) + (valueOther * (probOther / 100));
const expectedProfit = expectedRevenue - totalCost;
const expectedROI = totalCost > 0 ? ((expectedProfit / totalCost) * 100).toFixed(1) : 0;
// 格式化貨幣顯示
const formatMoney = (num) => {
return '$' + parseFloat(num).toLocaleString('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 0 });
};
return (
<div className="min-h-screen bg-slate-50 p-4 md:p-8 font-sans text-slate-800">
<div className="max-w-6xl mx-auto">
{/* 標題區 */}
<header className="mb-8 text-center md:text-left">
<div className="flex items-center justify-center md:justify-start gap-3 mb-2">
<Award className="w-8 h-8 text-yellow-500" />
<h1 className="text-3xl font-bold text-slate-900">卡牌評級回報估算機</h1>
</div>
<p className="text-slate-500">輸入卡牌成本、預計價值及獲取高分的機率,計算評級的真實期望回報。</p>
</header>
<div className="grid grid-cols-1 lg:grid-cols-12 gap-8">
{/* 左側:輸入區 */}
<div className="lg:col-span-5 space-y-6">
{/* 卡牌基本資料 */}
<div className="bg-white p-6 rounded-2xl shadow-sm border border-slate-100">
<h2 className="text-xl font-semibold mb-4 flex items-center gap-2">
<Calculator className="w-5 h-5 text-blue-500" /> 基本資料及成本
</h2>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-slate-600 mb-1">卡牌名稱</label>
<input
type="text"
value={cardName}
onChange={(e) => setCardName(e.target.value)}
className="w-full px-4 py-2 bg-slate-50 border border-slate-200 rounded-lg focus:ring-2 focus:ring-blue-500 focus:outline-none transition-all"
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-slate-600 mb-1">未評級裸卡成本 ($)</label>
<input
type="number"
value={rawCost}
onChange={(e) => setRawCost(e.target.value)}
className="w-full px-4 py-2 bg-slate-50 border border-slate-200 rounded-lg focus:ring-2 focus:ring-blue-500 focus:outline-none transition-all"
/>
</div>
<div>
<label className="block text-sm font-medium text-slate-600 mb-1">評級及運費 ($)</label>
<input
type="number"
value={gradingFee}
onChange={(e) => setGradingFee(e.target.value)}
className="w-full px-4 py-2 bg-slate-50 border border-slate-200 rounded-lg focus:ring-2 focus:ring-blue-500 focus:outline-none transition-all"
/>
</div>
</div>
</div>
</div>
{/* 評級後市場價值 */}
<div className="bg-white p-6 rounded-2xl shadow-sm border border-slate-100">
<h2 className="text-xl font-semibold mb-4 flex items-center gap-2">
<Coins className="w-5 h-5 text-yellow-500" /> 評級後市場價值
</h2>
<div className="space-y-4">
<div>
<label className="block text-sm font-bold text-yellow-600 mb-1">完美 10 分價值 (PSA/BGS 10)</label>
<input
type="number"
value={value10}
onChange={(e) => setValue10(e.target.value)}
className="w-full px-4 py-2 bg-yellow-50 border border-yellow-200 rounded-lg focus:ring-2 focus:ring-yellow-500 focus:outline-none transition-all font-semibold text-yellow-700"
/>
</div>
<div>
<label className="block text-sm font-bold text-slate-500 mb-1">優良 9 分價值 (PSA/BGS 9)</label>
<input
type="number"
value={value9}
onChange={(e) => setValue9(e.target.value)}
className="w-full px-4 py-2 bg-slate-50 border border-slate-200 rounded-lg focus:ring-2 focus:ring-slate-400 focus:outline-none transition-all font-semibold"
/>
</div>
<div>
<label className="block text-sm font-medium text-slate-500 mb-1">8 分或以下價值 (保底價)</label>
<input
type="number"
value={valueOther}
onChange={(e) => setValueOther(e.target.value)}
className="w-full px-4 py-2 bg-slate-50 border border-slate-200 rounded-lg focus:ring-2 focus:ring-slate-400 focus:outline-none transition-all"
/>
<p className="text-xs text-slate-400 mt-1">通常等同或略低於未評級的裸卡價格。</p>
</div>
</div>
</div>
{/* 評級機率預估 */}
<div className="bg-white p-6 rounded-2xl shadow-sm border border-slate-100">
<div className="flex items-center justify-between mb-4">
<h2 className="text-xl font-semibold flex items-center gap-2">
<TrendingUp className="w-5 h-5 text-indigo-500" /> 評級機率預估
</h2>
<div className="group relative">
<HelpCircle className="w-5 h-5 text-slate-400 cursor-pointer" />
<div className="absolute right-0 w-64 p-3 bg-slate-800 text-white text-xs rounded-lg shadow-xl opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all z-10 bottom-full mb-2">
根據您對卡牌狀態(白邊、置中、暗傷等)的觀察,預估獲得各分數的機率。這會用來計算「期望值」,幫助您作客觀決定。
</div>
</div>
</div>
<div className="space-y-6">
<div>
<div className="flex justify-between mb-1">
<label className="text-sm font-bold text-yellow-600">獲得 10 分機率</label>
<span className="text-sm font-bold text-yellow-600">{prob10}%</span>
</div>
<input
type="range"
min="0" max="100"
value={prob10}
onChange={handleProb10Change}
className="w-full h-2 bg-slate-200 rounded-lg appearance-none cursor-pointer accent-yellow-500"
/>
</div>
<div>
<div className="flex justify-between mb-1">
<label className="text-sm font-bold text-slate-600">獲得 9 分機率</label>
<span className="text-sm font-bold text-slate-600">{prob9}%</span>
</div>
<input
type="range"
min="0" max="100"
value={prob9}
onChange={handleProb9Change}
className="w-full h-2 bg-slate-200 rounded-lg appearance-none cursor-pointer accent-slate-500"
/>
</div>
<div className="p-3 bg-slate-50 rounded-lg border border-slate-200 flex justify-between items-center">
<span className="text-sm text-slate-500">獲得 8 分或以下機率 (自動計算)</span>
<span className="font-medium text-slate-600">{probOther}%</span>
</div>
</div>
</div>
</div>
{/* 右側:分析結果區 */}
<div className="lg:col-span-7 space-y-6">
{/* 總成本顯示 */}
<div className="bg-slate-900 text-white p-6 rounded-2xl shadow-lg flex flex-col md:flex-row justify-between items-center gap-4">
<div>
<p className="text-slate-400 text-sm mb-1">評級總成本 (裸卡 + 評級費)</p>
<p className="text-3xl font-bold">{formatMoney(totalCost)}</p>
</div>
<div className="text-right">
<p className="text-slate-400 text-sm mb-1">目標卡牌</p>
<p className="text-xl font-medium text-blue-300">{cardName}</p>
</div>
</div>
{/* 情境分析 */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* 情境 A: 10分 */}
<div className="bg-white p-6 rounded-2xl shadow-sm border-2 border-yellow-200 relative overflow-hidden">
<div className="absolute top-0 right-0 bg-yellow-100 text-yellow-800 text-xs font-bold px-3 py-1 rounded-bl-lg">
完美情境
</div>
<h3 className="text-lg font-bold text-slate-800 mb-1">成功獲取 10 分</h3>
<p className="text-sm text-slate-500 mb-4">回報最豐厚的情況</p>
<div className="space-y-3">
<div className="flex justify-between items-center">
<span className="text-slate-500 text-sm">預期價值</span>
<span className="font-semibold">{formatMoney(value10)}</span>
</div>
<div className="flex justify-between items-center pb-3 border-b border-slate-100">
<span className="text-slate-500 text-sm">總成本</span>
<span className="text-red-500 font-medium">-{formatMoney(totalCost)}</span>
</div>
<div className="flex justify-between items-center">
<span className="font-bold text-slate-700">淨利潤</span>
<span className={`text-xl font-bold ${profit10 >= 0 ? 'text-green-600' : 'text-red-600'}`}>
{profit10 > 0 ? '+' : ''}{formatMoney(profit10)}
</span>
</div>
<div className="flex justify-between items-center pt-2">
<span className="text-slate-500 text-sm">回報率 (ROI)</span>
<span className={`font-bold px-2 py-1 rounded text-sm ${profit10 >= 0 ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'}`}>
{profit10 > 0 ? '+' : ''}{roi10}%
</span>
</div>
</div>
</div>
{/* 情境 B: 9分 */}
<div className="bg-white p-6 rounded-2xl shadow-sm border-2 border-slate-200 relative overflow-hidden">
<div className="absolute top-0 right-0 bg-slate-100 text-slate-600 text-xs font-bold px-3 py-1 rounded-bl-lg">
常見情境
</div>
<h3 className="text-lg font-bold text-slate-800 mb-1">獲取 9 分</h3>
<p className="text-sm text-slate-500 mb-4">較保守的安全網</p>
<div className="space-y-3">
<div className="flex justify-between items-center">
<span className="text-slate-500 text-sm">預期價值</span>
<span className="font-semibold">{formatMoney(value9)}</span>
</div>
<div className="flex justify-between items-center pb-3 border-b border-slate-100">
<span className="text-slate-500 text-sm">總成本</span>
<span className="text-red-500 font-medium">-{formatMoney(totalCost)}</span>
</div>
<div className="flex justify-between items-center">
<span className="font-bold text-slate-700">淨利潤</span>
<span className={`text-xl font-bold ${profit9 >= 0 ? 'text-green-600' : 'text-red-600'}`}>
{profit9 > 0 ? '+' : ''}{formatMoney(profit9)}
</span>
</div>
<div className="flex justify-between items-center pt-2">
<span className="text-slate-500 text-sm">回報率 (ROI)</span>
<span className={`font-bold px-2 py-1 rounded text-sm ${profit9 >= 0 ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'}`}>
{profit9 > 0 ? '+' : ''}{roi9}%
</span>
</div>
</div>
</div>
</div>
{/* 期望值大數據總結 */}
<div className={`p-8 rounded-2xl shadow-md border-2 transition-colors ${expectedProfit >= 0 ? 'bg-emerald-50 border-emerald-200' : 'bg-rose-50 border-rose-200'}`}>
<div className="flex items-start gap-4">
<div className={`p-3 rounded-xl ${expectedProfit >= 0 ? 'bg-emerald-100 text-emerald-600' : 'bg-rose-100 text-rose-600'}`}>
{expectedProfit >= 0 ? <TrendingUp className="w-8 h-8" /> : <AlertCircle className="w-8 h-8" />}
</div>
<div className="flex-1">
<h3 className="text-2xl font-bold text-slate-800 mb-2">綜合期望回報 (Expected Value)</h3>
<p className="text-slate-600 mb-6 text-sm">
這代表如果這張卡牌的狀態在平行時空拿去評級 100 次,結合 {prob10}% 拿十分、{prob9}% 拿九分的機率,您平均每次評級的預計利潤。
</p>
<div className="grid grid-cols-2 gap-4">
<div className="bg-white p-4 rounded-xl shadow-sm">
<p className="text-sm text-slate-500 mb-1">預計平均淨利</p>
<p className={`text-3xl font-bold ${expectedProfit >= 0 ? 'text-emerald-600' : 'text-rose-600'}`}>
{expectedProfit > 0 ? '+' : ''}{formatMoney(expectedProfit)}
</p>
</div>
<div className="bg-white p-4 rounded-xl shadow-sm">
<p className="text-sm text-slate-500 mb-1">預計平均回報率</p>
<p className={`text-3xl font-bold ${expectedProfit >= 0 ? 'text-emerald-600' : 'text-rose-600'}`}>
{expectedProfit > 0 ? '+' : ''}{expectedROI}%
</p>
</div>
</div>
<div className="mt-6 pt-4 border-t border-slate-200/60">
<p className="font-medium text-slate-800">
AI 建議結論:
{expectedProfit > totalCost ?
<span className="text-emerald-600 ml-2">強烈建議評級!期望利潤極高。</span> :
expectedProfit > 0 ?
<span className="text-blue-600 ml-2">值得一試,整體期望值為正數。</span> :
<span className="text-rose-600 ml-2">不建議評級,風險大於回報,建議直接出售裸卡。</span>
}
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}