import { useState, useEffect, useCallback } from 'react'; import { fetchPredictionAnalyses } from '../services/predictionApi'; import type { PredictionAnalysis } from '../services/predictionApi'; export type Analysis = PredictionAnalysis; interface AnalysisListTableProps { onTabChange: (tab: string) => void; onSelectAnalysis?: (analysis: Analysis) => void; } export function AnalysisListTable({ onTabChange, onSelectAnalysis }: AnalysisListTableProps) { const [analyses, setAnalyses] = useState([]); const [loading, setLoading] = useState(true); const [searchTerm, setSearchTerm] = useState(''); const [currentPage, setCurrentPage] = useState(1); const itemsPerPage = 10; const loadData = useCallback(async () => { setLoading(true); try { const items = await fetchPredictionAnalyses({ search: searchTerm || undefined }); setAnalyses(items); } catch (err) { console.error('[prediction] 분석 목록 조회 실패:', err); } finally { setLoading(false); } }, [searchTerm]); useEffect(() => { loadData(); }, [loadData]); const getStatusBadge = (status: string) => { switch (status) { case 'completed': return ( 완료 ); case 'running': return ( 실행중 ); case 'pending': return ( 대기 ); case 'error': return ( 오류 ); case 'failed': return ( 실패 ); default: return null; } }; const totalPages = Math.ceil(analyses.length / itemsPerPage); const startIndex = (currentPage - 1) * itemsPerPage; const endIndex = startIndex + itemsPerPage; const currentAnalyses = analyses.slice(startIndex, endIndex); const renderPageNumbers = () => { const pages = []; const maxVisible = 5; if (totalPages <= maxVisible) { for (let i = 1; i <= totalPages; i++) { pages.push(i); } } else { if (currentPage <= 3) { for (let i = 1; i <= 5; i++) pages.push(i); pages.push('...'); pages.push(totalPages); } else if (currentPage >= totalPages - 2) { pages.push(1); pages.push('...'); for (let i = totalPages - 4; i <= totalPages; i++) pages.push(i); } else { pages.push(1); pages.push('...'); for (let i = currentPage - 1; i <= currentPage + 1; i++) pages.push(i); pages.push('...'); pages.push(totalPages); } } return pages.map((page, index) => { if (page === '...') { return ( ... ); } return ( ); }); }; return (
{/* 헤더 */}

유출유 확산 예측 목록

총 {analyses.length}건

setSearchTerm(e.target.value)} className="w-64 px-4 py-2 text-body-2 bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none" />
{/* 테이블 */}
{loading ? (
로딩 중...
) : ( {currentAnalyses.map((analysis) => ( ))}
번호 사고명 사고일시 예측 실행 예측시간 유종 유출량 KOSPS POSEIDON OpenDrift 역추적 담당자 소속
{analysis.acdntSn}
{ e.stopPropagation(); if (onSelectAnalysis) { onSelectAnalysis(analysis); } }} > {analysis.acdntNm}
{analysis.occurredAt ? new Date(analysis.occurredAt).toLocaleString('ko-KR', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', }) : '—'} {analysis.runDtm ? new Date(analysis.runDtm).toLocaleString('ko-KR', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', }) : '—'} {analysis.duration} {analysis.oilType} {analysis.volume != null ? analysis.volume >= 0.01 ? analysis.volume.toFixed(2) : analysis.volume.toExponential(2) : '—'} {getStatusBadge(analysis.kospsStatus)} {getStatusBadge(analysis.poseidonStatus)} {getStatusBadge(analysis.opendriftStatus)} {getStatusBadge(analysis.backtrackStatus)} {analysis.analyst} {analysis.officeName}
)} {!loading && analyses.length === 0 && (
분석 데이터가 없습니다.
)}
{/* 페이지네이션 */}
{renderPageNumbers()}
); }