ge-tool/components/CheckHistory.tsx
2025-12-10 13:41:43 +07:00

229 lines
14 KiB
TypeScript
Executable File

import React, { useState } from 'react';
interface CheckResult {
ge_id: string;
lang: string;
chapter: string;
status: 'FOUND' | 'NOT_FOUND' | 'ERROR';
message: string;
tms_url?: string;
}
interface CheckRecord {
id: string;
created_at: string;
input: any;
status: 'pending' | 'processing' | 'completed' | 'failed';
results: CheckResult[];
error?: string;
}
interface CheckHistoryProps {
history: CheckRecord[];
onDelete?: (id: string) => void;
}
const CheckHistory: React.FC<CheckHistoryProps> = ({ history, onDelete }) => {
const [expandedId, setExpandedId] = useState<string | null>(null);
const [hideCompleted, setHideCompleted] = useState(false);
const formatDate = (dateStr: string) => {
const date = new Date(dateStr);
return date.toLocaleString('vi-VN');
};
const getStatusColor = (status: string) => {
switch (status) {
case 'completed': return 'text-green-400';
case 'processing': return 'text-yellow-400';
case 'failed': return 'text-red-400';
default: return 'text-slate-400';
}
};
const getStatusText = (status: string) => {
switch (status) {
case 'completed': return 'Hoàn thành';
case 'processing': return 'Đang xử lý';
case 'failed': return 'Thất bại';
default: return 'Chờ xử lý';
}
};
return (
<div className="w-full mt-8">
<div className="flex justify-between items-center mb-6">
<h2 className="text-2xl font-semibold text-white">Lịch sử Check Upload</h2>
<label className="flex items-center gap-2 text-sm text-slate-400 cursor-pointer">
<span>n chap đã hoàn thành</span>
<button
onClick={() => setHideCompleted(!hideCompleted)}
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${hideCompleted ? 'bg-indigo-600' : 'bg-slate-600'
}`}
>
<span
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${hideCompleted ? 'translate-x-6' : 'translate-x-1'
}`}
/>
</button>
</label>
</div>
{history.length > 0 ? (
<div className="space-y-4">
{history.map((record) => {
const isExpanded = expandedId === record.id;
const foundCount = record.results.filter(r => r.status === 'FOUND').length;
const notFoundCount = record.results.filter(r => r.status === 'NOT_FOUND').length;
const errorCount = record.results.filter(r => r.status === 'ERROR').length;
// Filter results based on toggle - hide FOUND (completed) when toggle is on
let displayResults = hideCompleted
? record.results.filter(r => r.status !== 'FOUND')
: record.results;
// Sort results by GE ID (ascending)
displayResults = [...displayResults].sort((a, b) => {
const aId = parseInt(a.ge_id) || 0;
const bId = parseInt(b.ge_id) || 0;
return aId - bId;
});
// Skip record if no results to show when filter is on
if (hideCompleted && displayResults.length === 0) {
return null;
}
return (
<div
key={record.id}
className="bg-slate-800/50 border border-slate-700 rounded-lg overflow-hidden"
>
<div
className="p-4 cursor-pointer hover:bg-slate-700/30 transition-colors"
onClick={() => setExpandedId(isExpanded ? null : record.id)}
>
<div className="flex justify-between items-start">
<div className="flex-1">
<div className="flex items-center gap-3 mb-2">
<span className="text-sm text-slate-400">{formatDate(record.created_at)}</span>
<span className={`text-sm font-medium ${getStatusColor(record.status)}`}>
{getStatusText(record.status)}
</span>
</div>
<div className="flex gap-4 text-sm">
<span className="text-slate-300">
Tổng: <span className="font-medium text-white">{record.results.length}</span>
</span>
{foundCount > 0 && (
<span className="text-green-400">
Đã : <span className="font-medium">{foundCount}</span>
</span>
)}
{notFoundCount > 0 && (
<span className="text-yellow-400">
Chưa : <span className="font-medium">{notFoundCount}</span>
</span>
)}
{errorCount > 0 && (
<span className="text-red-400">
Lỗi: <span className="font-medium">{errorCount}</span>
</span>
)}
</div>
</div>
<div className="flex items-center gap-2">
{onDelete && (
<button
onClick={(e) => {
e.stopPropagation();
onDelete(record.id);
}}
className="text-slate-500 hover:text-red-400 transition-colors p-1"
title="Xoá"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
)}
<button className="text-slate-400 hover:text-white transition-colors">
<svg
className={`w-5 h-5 transition-transform ${isExpanded ? 'rotate-180' : ''}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</button>
</div>
</div>
</div>
{isExpanded && displayResults.length > 0 && (
<div className="border-t border-slate-700">
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead className="bg-slate-700/50">
<tr>
<th className="py-2 px-4 text-left text-slate-300">GE ID</th>
<th className="py-2 px-4 text-left text-slate-300">LANG</th>
<th className="py-2 px-4 text-left text-slate-300">CHAP</th>
<th className="py-2 px-4 text-left text-slate-300">Trạng thái</th>
<th className="py-2 px-4 text-left text-slate-300">Ghi chú</th>
</tr>
</thead>
<tbody>
{displayResults.map((result, idx) => (
<tr key={idx} className="border-t border-slate-700/50 hover:bg-slate-700/30">
<td className="py-2 px-4">
{result.tms_url ? (
<a
href={result.tms_url}
target="_blank"
rel="noreferrer"
className="text-blue-400 hover:underline"
>
{result.ge_id}
</a>
) : (
<span className="text-white">{result.ge_id}</span>
)}
</td>
<td className="py-2 px-4 text-slate-300">{result.lang}</td>
<td className="py-2 px-4 text-slate-300">{result.chapter}</td>
<td className="py-2 px-4">
<span
className={`px-2 py-1 rounded text-xs font-bold ${result.status === 'FOUND'
? 'bg-green-900 text-green-300'
: result.status === 'NOT_FOUND'
? 'bg-yellow-900 text-yellow-300'
: 'bg-red-900 text-red-300'
}`}
>
{result.status === 'FOUND' ? 'ĐÃ CÓ' : result.status === 'NOT_FOUND' ? 'CHƯA CÓ' : 'LỖI'}
</span>
</td>
<td className="py-2 px-4 text-slate-400">{result.message}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
</div>
);
})}
</div>
) : (
<div className="text-center py-10 px-6 bg-slate-800/50 border border-slate-700 rounded-lg">
<p className="text-slate-400">Chưa lịch sử check.</p>
</div>
)}
</div>
);
};
export default CheckHistory;