first commit

This commit is contained in:
2026-01-09 19:14:41 +08:00
commit 9f3c96ce73
67 changed files with 9636 additions and 0 deletions

654
SampleOrderAssistant.txt Normal file
View File

@@ -0,0 +1,654 @@
import React, { useState, useEffect } from 'react';
import {
Upload, FileText, Database, CheckCircle, XCircle,
AlertTriangle, BarChart2, PieChart, Activity,
ArrowRight, Search, Filter, Download, RefreshCw, FileSpreadsheet,
Info
} from 'lucide-react';
import {
BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip as RechartsTooltip, Legend, ResponsiveContainer,
PieChart as RePieChart, Pie, Cell
} from 'recharts';
// --- 真實模擬資料 (源自使用者上傳檔案) ---
// 1. DIT Report (模擬解析後的結構)
// 來源: DIT report (by Focus Item)_app_樣本.xlsx
const PARSED_DIT_DATA = [
{ id: 'OP0000012868', customer: '光寶科技股份有限公司', pn: 'GBU610', eau: 20000, stage: 'Design Win', date: '2025-02-04' },
{ id: 'OP0000012869', customer: 'LITEON (PMS)', pn: 'PMSM808LL_R2', eau: 55000, stage: 'Design-In', date: '2025-07-28' },
{ id: 'OP0000012870', customer: '台達電子', pn: 'PEC3205ES-AU', eau: 100000, stage: 'Sample Provide', date: '2025-08-15' }, // 故意用簡稱測試模糊比對
{ id: 'OP0000012871', customer: '合美企業', pn: 'PJT7828', eau: 5000, stage: 'Negotiation', date: '2025-08-10' },
{ id: 'OP0000012872', customer: '申浦电子', pn: 'ER1604FCT', eau: 30000, stage: 'Design Win', date: '2025-07-30' },
];
// 2. Sample Data (模擬解析後的結構)
// 來源: 樣品申請紀錄_樣本.xlsx
const PARSED_SAMPLE_DATA = [
{ id: 'S202512490-02', order_no: 'S202512490', customer: '台達電子工業股份有限公司', pn: 'PEC3205ES-AU', qty: 20, date: '2025-12-19' },
{ id: 'S202512490-03', order_no: 'S202512490', customer: '台達電子工業股份有限公司', pn: 'PDZ9.1B-AU', qty: 20, date: '2025-12-19' },
];
// 3. Order Data (模擬解析後的結構)
// 來源: 訂單樣本_20251217.xlsx
const PARSED_ORDER_DATA = [
{ id: '1125025312-1', order_no: '1125025312', customer: '合美企業有限公司', pn: 'PJT7828', qty: 1200, status: 'Shipped', amount: 10800 },
{ id: '1125062105-1', order_no: '1125062105', customer: '申浦电子', pn: 'ER1604FCT', qty: 800, status: 'Backlog', amount: 3200 },
];
// --- 模糊比對模擬結果 ---
const SIMULATED_REVIEWS = [
{
id: 101,
score: 88,
dit: { id: 'OP0000012870', cust: '台達電子', pn: 'PEC3205ES-AU' },
match_target: { id: 'S202512490', cust: '台達電子工業股份有限公司', pn: 'PEC3205ES-AU', type: 'SAMPLE' },
type: 'SAMPLE',
reason: 'Customer Name Partial Match (88%)'
},
{
id: 102,
score: 92,
dit: { id: 'OP0000012871', cust: '合美企業', pn: 'PJT7828' },
match_target: { id: '1125025312', cust: '合美企業有限公司', pn: 'PJT7828', type: 'ORDER' },
type: 'ORDER',
reason: 'Corporate Suffix Mismatch'
}
];
// --- 輔助元件 ---
const Card = ({ children, className = "" }) => (
<div className={`bg-white rounded-lg border border-slate-200 shadow-sm ${className}`}>
{children}
</div>
);
const Badge = ({ children, type = "neutral" }) => {
const styles = {
neutral: "bg-slate-100 text-slate-600",
success: "bg-emerald-100 text-emerald-700",
warning: "bg-amber-100 text-amber-700",
danger: "bg-rose-100 text-rose-700",
info: "bg-blue-100 text-blue-700"
};
return (
<span className={`px-2 py-1 rounded-full text-xs font-medium ${styles[type]}`}>
{children}
</span>
);
};
// --- 主應用程式 ---
export default function App() {
const [activeTab, setActiveTab] = useState('import');
const [isProcessing, setIsProcessing] = useState(false);
const [processingStep, setProcessingStep] = useState('');
const [filesLoaded, setFilesLoaded] = useState(false);
// 狀態管理
const [reviews, setReviews] = useState([]);
const [processedCount, setProcessedCount] = useState(0);
// Dashboard 數據
const [dashboardData, setDashboardData] = useState({
totalDit: 5,
matchedSample: 0,
matchedOrder: 1, // '申浦电子' 是 Exact Match
conversionRate: 20.0
});
// 模擬載入使用者的真實檔案
const loadUserFiles = () => {
setFilesLoaded(true);
};
// 模擬 ETL 執行
const runEtl = () => {
if (!filesLoaded) {
alert("請先載入檔案!");
return;
}
setIsProcessing(true);
// 模擬後端處理步驟
const steps = [
"正在讀取 DIT Report... 偵測到表頭在第 16 行",
"正在讀取 樣品紀錄... 偵測到表頭在第 9 行",
"正在讀取 訂單明細... 編碼識別為 CP950",
"執行資料標準化 (Normalization)...",
"執行模糊比對 (Fuzzy Matching)...",
"運算完成!"
];
let stepIndex = 0;
const interval = setInterval(() => {
if (stepIndex >= steps.length) {
clearInterval(interval);
setIsProcessing(false);
setReviews(SIMULATED_REVIEWS); // 載入模擬的比對結果
setActiveTab('review');
} else {
setProcessingStep(steps[stepIndex]);
stepIndex++;
}
}, 800);
};
// 處理審核動作
const handleReviewAction = (id, action) => {
const reviewItem = reviews.find(r => r.id === id);
setReviews(prev => prev.filter(r => r.id !== id));
if (action === 'accept') {
setProcessedCount(prev => prev + 1);
// 更新 Dashboard 數據 (模擬)
if (reviewItem.type === 'ORDER') {
setDashboardData(prev => ({
...prev,
matchedOrder: prev.matchedOrder + 1,
conversionRate: ((prev.matchedOrder + 1) / prev.totalDit * 100).toFixed(1)
}));
} else if (reviewItem.type === 'SAMPLE') {
setDashboardData(prev => ({
...prev,
matchedSample: prev.matchedSample + 1
}));
}
}
};
// 漏斗圖資料
const funnelData = [
{ name: 'DIT 案件', value: dashboardData.totalDit, fill: '#6366f1' },
{ name: '成功送樣', value: dashboardData.matchedSample, fill: '#8b5cf6' },
{ name: '取得訂單', value: dashboardData.matchedOrder, fill: '#10b981' },
];
return (
<div className="min-h-screen bg-slate-50 font-sans text-slate-800">
{/* Top Navigation */}
<header className="bg-white border-b border-slate-200 sticky top-0 z-10">
<div className="max-w-7xl mx-auto px-4 h-16 flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-indigo-600 rounded-lg flex items-center justify-center text-white font-bold">
S
</div>
<div>
<span className="font-bold text-lg text-slate-800 block leading-tight">SalesPipeline</span>
<span className="text-[10px] text-slate-500 font-medium">On-Premise Simulator</span>
</div>
</div>
<div className="flex gap-1 bg-slate-100 p-1 rounded-lg">
{[
{ id: 'import', icon: Database, label: '資料匯入' },
{ id: 'review', icon: CheckCircle, label: '比對審核', badge: reviews.length },
{ id: 'dashboard', icon: BarChart2, label: '分析儀表板' }
].map(tab => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`
flex items-center gap-2 px-4 py-2 rounded-md text-sm font-medium transition-all
${activeTab === tab.id
? 'bg-white text-indigo-600 shadow-sm'
: 'text-slate-500 hover:text-slate-700 hover:bg-slate-200/50'}
`}
>
<tab.icon size={16} />
{tab.label}
{tab.badge > 0 && (
<span className="bg-rose-500 text-white text-[10px] px-1.5 py-0.5 rounded-full">
{tab.badge}
</span>
)}
</button>
))}
</div>
<div className="flex items-center gap-2 text-sm text-emerald-600 bg-emerald-50 px-3 py-1 rounded-full border border-emerald-100">
<div className="w-2 h-2 rounded-full bg-emerald-500 animate-pulse"></div>
系統運作中
</div>
</div>
</header>
<main className="max-w-7xl mx-auto px-4 py-8">
{/* --- View 1: Import --- */}
{activeTab === 'import' && (
<div className="space-y-6 animate-in fade-in slide-in-from-bottom-4 duration-500">
<div className="flex justify-between items-end">
<div>
<h1 className="text-2xl font-bold text-slate-800">原始資料匯入中心</h1>
<p className="text-slate-500 mt-1">系統將自動偵測 Excel/CSV 檔頭位置並進行智慧欄位對應。</p>
</div>
<div className="flex gap-3">
<button
onClick={loadUserFiles}
disabled={filesLoaded || isProcessing}
className={`flex items-center gap-2 px-4 py-3 rounded-lg font-medium border transition-all ${filesLoaded ? 'bg-slate-100 text-slate-400 border-slate-200' : 'bg-white text-slate-700 border-slate-300 hover:bg-slate-50'}`}
>
<FileSpreadsheet size={18} />
{filesLoaded ? '檔案已就緒' : '載入範本檔案'}
</button>
<button
onClick={runEtl}
disabled={isProcessing || !filesLoaded}
className={`flex items-center gap-2 px-6 py-3 rounded-lg text-white font-bold shadow-lg transition-all ${isProcessing || !filesLoaded ? 'bg-slate-400 cursor-not-allowed shadow-none' : 'bg-indigo-600 hover:bg-indigo-700 hover:scale-105 shadow-indigo-200'}`}
>
{isProcessing ? (
<>
<RefreshCw size={18} className="animate-spin" />
運算中...
</>
) : (
<>
<Activity size={18} />
開始 ETL 運算
</>
)}
</button>
</div>
</div>
{/* Progress Log */}
{isProcessing && (
<div className="bg-slate-800 text-green-400 font-mono p-4 rounded-lg text-sm shadow-inner">
<p className="flex items-center gap-2">
<span className="animate-pulse">▶</span> {processingStep}
</p>
</div>
)}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{[
{ type: 'dit', title: '1. DIT Report', file: 'DIT report_app_樣本.xlsx', desc: '含 Metadata (前16行)', count: '5 筆 (測試)' },
{ type: 'sample', title: '2. 樣品紀錄', file: '樣品申請紀錄_樣本.xlsx', desc: '含檔頭 (前9行)', count: '2 筆 (測試)' },
{ type: 'order', title: '3. 訂單明細', file: '訂單樣本_20251217.xlsx', desc: '標準格式', count: '2 筆 (測試)' },
].map(file => (
<Card key={file.type} className={`p-6 border-dashed border-2 transition-colors ${filesLoaded ? 'border-emerald-300 bg-emerald-50/30' : 'border-slate-300'}`}>
<div className="flex justify-between items-start mb-4">
<div className={`p-3 rounded-lg ${filesLoaded ? 'bg-emerald-100 text-emerald-600' : 'bg-slate-100 text-slate-500'}`}>
{filesLoaded ? <CheckCircle size={24} /> : <FileText size={24} />}
</div>
{filesLoaded && <Badge type="success">Ready</Badge>}
</div>
<h3 className="text-lg font-bold text-slate-800">{file.title}</h3>
<p className="text-xs text-slate-500 font-mono mt-1 mb-3 truncate">{filesLoaded ? file.file : "等待上傳..."}</p>
<p className="text-sm text-slate-600">{file.desc}</p>
{filesLoaded && (
<div className="mt-4 pt-3 border-t border-slate-200/60 flex justify-between text-sm">
<span className="text-slate-500">預覽筆數</span>
<span className="font-bold font-mono text-slate-700">{file.count}</span>
</div>
)}
</Card>
))}
</div>
{/* Data Preview (Mocked from User Files) */}
{filesLoaded && (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 animate-in fade-in slide-in-from-bottom-2 duration-500">
<Card className="overflow-hidden">
<div className="bg-slate-50 px-4 py-3 border-b border-slate-200 flex justify-between items-center">
<h3 className="font-bold text-slate-700 text-sm flex items-center gap-2">
<Database size={16} />
DIT 解析結果 (預覽)
</h3>
<Badge type="info">Auto-Skipped Header</Badge>
</div>
<table className="w-full text-xs text-left">
<thead className="bg-white text-slate-500 border-b border-slate-200">
<tr>
<th className="px-4 py-2">Customer</th>
<th className="px-4 py-2">Part No</th>
<th className="px-4 py-2">Stage</th>
<th className="px-4 py-2 text-right">EAU</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-100">
{PARSED_DIT_DATA.map((row, i) => (
<tr key={i} className="hover:bg-slate-50">
<td className="px-4 py-2 font-medium text-slate-700">{row.customer}</td>
<td className="px-4 py-2 font-mono text-slate-500">{row.pn}</td>
<td className="px-4 py-2 text-slate-600">{row.stage}</td>
<td className="px-4 py-2 text-right font-mono">{row.eau.toLocaleString()}</td>
</tr>
))}
</tbody>
</table>
</Card>
<Card className="overflow-hidden">
<div className="bg-slate-50 px-4 py-3 border-b border-slate-200 flex justify-between items-center">
<h3 className="font-bold text-slate-700 text-sm flex items-center gap-2">
<Database size={16} />
訂單解析結果 (預覽)
</h3>
<Badge type="info">Detected CP950</Badge>
</div>
<table className="w-full text-xs text-left">
<thead className="bg-white text-slate-500 border-b border-slate-200">
<tr>
<th className="px-4 py-2">Customer</th>
<th className="px-4 py-2">Part No</th>
<th className="px-4 py-2">Status</th>
<th className="px-4 py-2 text-right">Qty</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-100">
{PARSED_ORDER_DATA.map((row, i) => (
<tr key={i} className="hover:bg-slate-50">
<td className="px-4 py-2 font-medium text-slate-700">{row.customer}</td>
<td className="px-4 py-2 font-mono text-slate-500">{row.pn}</td>
<td className="px-4 py-2">
<span className={`px-1.5 py-0.5 rounded text-[10px] ${row.status === 'Shipped' ? 'bg-green-100 text-green-700' : 'bg-pink-100 text-pink-700'}`}>
{row.status}
</span>
</td>
<td className="px-4 py-2 text-right font-mono">{row.qty.toLocaleString()}</td>
</tr>
))}
</tbody>
</table>
</Card>
</div>
)}
</div>
)}
{/* --- View 2: Review --- */}
{activeTab === 'review' && (
<div className="space-y-6 animate-in fade-in zoom-in-95 duration-300">
<div className="flex justify-between items-center">
<div>
<h1 className="text-2xl font-bold text-slate-800 flex items-center gap-2">
模糊比對審核工作台
<span className="text-sm bg-indigo-100 text-indigo-700 px-2 py-1 rounded-full font-normal">待審核: {reviews.length}</span>
</h1>
<p className="text-slate-500 mt-1">系統發現以下案件名稱相似,請人工確認關聯性。</p>
</div>
</div>
{reviews.length === 0 ? (
<div className="flex flex-col items-center justify-center py-20 bg-white rounded-lg border border-dashed border-slate-300">
<div className="w-16 h-16 bg-emerald-100 text-emerald-600 rounded-full flex items-center justify-center mb-4">
<CheckCircle size={32} />
</div>
<h3 className="text-xl font-bold text-slate-800">所有案件已審核完畢!</h3>
<p className="text-slate-500 mt-2">您的資料比對已完成,請查看分析儀表板。</p>
<button
onClick={() => setActiveTab('dashboard')}
className="mt-6 px-6 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors"
>
前往儀表板
</button>
</div>
) : (
<div className="grid gap-4">
{reviews.map(item => (
<Card key={item.id} className="p-0 overflow-hidden group hover:shadow-md transition-all border-l-4 border-l-amber-400">
<div className="flex flex-col md:flex-row">
{/* Left: DIT */}
<div className="flex-1 p-5 border-b md:border-b-0 md:border-r border-slate-100 bg-slate-50/50">
<div className="flex items-center gap-2 mb-2">
<Badge type="info">DIT (設計導入)</Badge>
<span className="text-xs text-slate-400">OP編號: {item.dit.id}</span>
</div>
<div className="space-y-1">
<div className="text-xs text-slate-400 uppercase">Customer Name</div>
<div className="font-bold text-slate-800 text-lg">{item.dit.cust}</div>
<div className="text-xs text-slate-400 uppercase mt-2">Part Number</div>
<div className="font-mono text-slate-700 bg-white border border-slate-200 px-2 py-1 rounded inline-block text-sm">
{item.dit.pn}
</div>
</div>
</div>
{/* Middle: Score & Reason */}
<div className="w-full md:w-48 p-4 flex flex-col items-center justify-center bg-white z-10">
<div className="text-center">
<div className="text-2xl font-bold text-amber-500 mb-1">{item.score}%</div>
<div className="text-[10px] font-medium text-amber-600 bg-amber-50 px-2 py-0.5 rounded-full mb-2">
相似度
</div>
<div className="text-xs text-slate-400 text-center px-2 leading-tight">
{item.reason}
</div>
</div>
</div>
{/* Right: Target (Sample/Order) */}
<div className="flex-1 p-5 border-t md:border-t-0 md:border-l border-slate-100 bg-indigo-50/30">
<div className="flex items-center gap-2 mb-2">
<Badge type={item.type === 'ORDER' ? 'warning' : 'success'}>
{item.type === 'ORDER' ? 'Order (訂單)' : 'Sample (樣品)'}
</Badge>
<span className="text-xs text-slate-400">來源單號: {item.match_target.id}</span>
</div>
<div className="space-y-1">
<div className="text-xs text-slate-400 uppercase">Customer Name</div>
<div className="font-bold text-slate-800 text-lg">{item.match_target.cust}</div>
<div className="text-xs text-slate-400 uppercase mt-2">Part Number</div>
<div className="font-mono text-slate-700 bg-white border border-slate-200 px-2 py-1 rounded inline-block text-sm">
{item.match_target.pn}
</div>
</div>
</div>
{/* Actions */}
<div className="w-full md:w-40 p-4 bg-slate-50 flex flex-row md:flex-col gap-3 justify-center items-center border-t md:border-t-0 md:border-l border-slate-200">
<button
onClick={() => handleReviewAction(item.id, 'accept')}
className="flex-1 md:flex-none w-full py-2 bg-emerald-600 hover:bg-emerald-700 text-white rounded-lg text-sm font-medium flex items-center justify-center gap-2 transition-colors shadow-sm"
>
<CheckCircle size={16} /> 確認關聯
</button>
<button
onClick={() => handleReviewAction(item.id, 'reject')}
className="flex-1 md:flex-none w-full py-2 bg-white border border-slate-300 text-slate-600 hover:bg-slate-50 rounded-lg text-sm font-medium flex items-center justify-center gap-2 transition-colors"
>
<XCircle size={16} /> 駁回
</button>
</div>
</div>
</Card>
))}
</div>
)}
</div>
)}
{/* --- View 3: Dashboard --- */}
{activeTab === 'dashboard' && (
<div className="space-y-6 animate-in fade-in slide-in-from-right-4 duration-500">
<div className="flex justify-between items-center">
<div>
<h1 className="text-2xl font-bold text-slate-800">業務轉換率戰情室</h1>
<p className="text-slate-500 mt-1">數據來源DIT Report, 樣品紀錄, 訂單明細 (2025-12-17)</p>
</div>
<button className="flex items-center gap-2 px-4 py-2 border border-slate-300 rounded-lg text-slate-600 hover:bg-slate-50 text-sm font-medium">
<Download size={16} />
匯出報表
</button>
</div>
{/* KPI Cards */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
<Card className="p-5 border-l-4 border-l-indigo-500">
<div className="text-sm text-slate-500 mb-1">DIT 總案件數</div>
<div className="text-3xl font-bold text-slate-800">{dashboardData.totalDit}</div>
<div className="text-xs text-slate-400 mt-2">Raw Data Count</div>
</Card>
<Card className="p-5 border-l-4 border-l-purple-500">
<div className="text-sm text-slate-500 mb-1">成功送樣數</div>
<div className="text-3xl font-bold text-slate-800">{dashboardData.matchedSample}</div>
<div className="text-xs text-emerald-600 mt-2">
{processedCount > 0 ? `(含人工審核 +1)` : ''}
</div>
</Card>
<Card className="p-5 border-l-4 border-l-pink-500">
<div className="text-sm text-slate-500 mb-1">取得訂單 (Backlog)</div>
<div className="text-3xl font-bold text-slate-800">{dashboardData.matchedOrder}</div>
<div className="text-xs text-emerald-600 mt-2">
Match Rate: {dashboardData.conversionRate}%
</div>
</Card>
<Card className="p-5 border-l-4 border-l-emerald-500">
<div className="text-sm text-slate-500 mb-1">預估營收 (Revenue)</div>
<div className="text-3xl font-bold text-emerald-600">$14,000</div>
<div className="text-xs text-slate-400 mt-2">Based on matched orders</div>
</Card>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Funnel Chart */}
<Card className="lg:col-span-2 p-6">
<h3 className="font-bold text-slate-700 mb-6 flex items-center gap-2">
<Filter size={18} />
DIT 轉換漏斗 (Funnel Analysis)
</h3>
<div className="h-64 w-full">
<ResponsiveContainer width="100%" height="100%">
<BarChart
layout="vertical"
data={funnelData}
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
>
<CartesianGrid strokeDasharray="3 3" horizontal={false} />
<XAxis type="number" />
<YAxis dataKey="name" type="category" width={100} />
<RechartsTooltip cursor={{fill: '#f1f5f9'}} />
<Bar dataKey="value" barSize={30} radius={[0, 4, 4, 0]} label={{ position: 'right' }}>
{funnelData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.fill} />
))}
</Bar>
</BarChart>
</ResponsiveContainer>
</div>
</Card>
{/* Status Pie Chart */}
<Card className="p-6">
<h3 className="font-bold text-slate-700 mb-6 flex items-center gap-2">
<PieChart size={18} />
訂單狀態佔比
</h3>
<div className="h-64 w-full flex justify-center">
<ResponsiveContainer width="100%" height="100%">
<RePieChart>
<Pie
data={[
{ name: 'Backlog', value: 800 },
{ name: 'Shipped', value: 1200 },
]}
cx="50%"
cy="50%"
innerRadius={60}
outerRadius={80}
paddingAngle={5}
dataKey="value"
>
<Cell fill="#ec4899" />
<Cell fill="#10b981" />
</Pie>
<RechartsTooltip />
<Legend verticalAlign="bottom" height={36}/>
</RePieChart>
</ResponsiveContainer>
</div>
</Card>
</div>
{/* Attribution Table Preview */}
<Card className="overflow-hidden">
<div className="bg-slate-50 px-6 py-4 border-b border-slate-200 flex justify-between items-center">
<h3 className="font-bold text-slate-700 flex items-center gap-2">
<Activity size={18} />
DIT 歸因明細表 (LIFO 邏輯)
</h3>
<div className="text-xs text-slate-500">
<span className="flex items-center gap-1">
<Info size={12} />
Hover to see order details
</span>
</div>
</div>
<table className="w-full text-sm text-left">
<thead className="bg-white text-slate-500 border-b border-slate-200">
<tr>
<th className="px-6 py-3 w-32">OP編號</th>
<th className="px-6 py-3">Customer</th>
<th className="px-6 py-3">Part No.</th>
<th className="px-6 py-3 text-right">EAU</th>
<th className="px-6 py-3 text-center">Sample Order</th>
<th className="px-6 py-3 text-center">Order Status</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-100">
{PARSED_DIT_DATA.map((row, i) => {
// 簡單模擬關聯邏輯 (Find exact matches or partial matches)
const matchedOrder = PARSED_ORDER_DATA.find(o => o.customer.includes(row.customer) || row.customer.includes(o.customer.substring(0, 2)));
const matchedSample = PARSED_SAMPLE_DATA.find(s => s.customer.includes(row.customer) || row.customer.includes(s.customer.substring(0, 2)));
// 人工審核修正 (台達電子 & 合美)
const isReviewedSample = (row.customer === '台達電子' && processedCount > 0) ? {order_no: 'S202512490'} : null;
const isReviewedOrder = (row.customer === '合美企業' && processedCount > 1) ? {order_no: '1125025312'} : null;
// 決定顯示內容
const finalSample = matchedSample || isReviewedSample;
const finalOrder = matchedOrder || isReviewedOrder;
return (
<tr key={i} className="hover:bg-slate-50 group transition-colors">
<td className="px-6 py-3 font-mono text-xs text-slate-500 font-bold">
{row.id}
</td>
<td className="px-6 py-3 font-medium text-slate-800">
{row.customer}
<div className="text-[10px] text-slate-400 font-light">{row.stage}</div>
</td>
<td className="px-6 py-3 font-mono text-slate-600 text-xs">{row.pn}</td>
<td className="px-6 py-3 text-right font-mono text-slate-600">{row.eau.toLocaleString()}</td>
{/* Sample Column */}
<td className="px-6 py-3 text-center">
{finalSample ? (
<div className="inline-flex items-center gap-1 px-2 py-1 bg-purple-50 text-purple-700 rounded-md text-xs font-mono border border-purple-100 cursor-help" title={`送樣單號: ${finalSample.order_no}`}>
<CheckCircle size={12} />
{finalSample.order_no}
</div>
) : (
<span className="text-slate-300">-</span>
)}
</td>
{/* Order Column */}
<td className="px-6 py-3 text-center">
{finalOrder ? (
<div className="inline-flex items-center gap-1 px-2 py-1 bg-emerald-50 text-emerald-700 rounded-md text-xs font-mono border border-emerald-100 cursor-help" title={`訂單單號: ${finalOrder.order_no}`}>
<CheckCircle size={12} />
{finalOrder.order_no}
</div>
) : (
<span className="text-slate-300">-</span>
)}
</td>
</tr>
)
})}
</tbody>
</table>
</Card>
</div>
)}
</main>
</div>
);
}