import React, { useState, useCallback, useEffect } from 'react'; import { Upload, FileText, AlertCircle, CheckCircle, Loader2, Database, TrendingUp, Settings, Eye, BarChart3, Filter } from 'lucide-react'; const FileUploadPage = () => { const [uploadStatus, setUploadStatus] = useState('idle'); // idle, uploading, analyzing, classifying, success, error const [uploadProgress, setUploadProgress] = useState(0); const [uploadResult, setUploadResult] = useState(null); const [dragActive, setDragActive] = useState(false); const [selectedProject, setSelectedProject] = useState(''); const [projects, setProjects] = useState([]); const [analysisStep, setAnalysisStep] = useState(''); const [classificationPreview, setClassificationPreview] = useState(null); // 프로젝트 목록 로드 useEffect(() => { fetchProjects(); }, []); const fetchProjects = async () => { try { const response = await fetch('http://localhost:8000/api/projects'); const data = await response.json(); setProjects(data); if (data.length > 0) { setSelectedProject(data[0].id.toString()); } } catch (error) { console.error('프로젝트 로드 실패:', error); } }; // 드래그 앤 드롭 핸들러 const handleDrag = useCallback((e) => { e.preventDefault(); e.stopPropagation(); if (e.type === "dragenter" || e.type === "dragover") { setDragActive(true); } else if (e.type === "dragleave") { setDragActive(false); } }, []); const handleDrop = useCallback((e) => { e.preventDefault(); e.stopPropagation(); setDragActive(false); if (e.dataTransfer.files && e.dataTransfer.files[0]) { handleFileUpload(e.dataTransfer.files[0]); } }, []); // 파일 업로드 및 4단계 자동 분류 처리 const handleFileUpload = async (file) => { if (!file) return; if (!selectedProject) { alert('프로젝트를 먼저 선택해주세요.'); return; } // 파일 타입 체크 (다양한 형식 지원) const allowedTypes = [ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // .xlsx 'application/vnd.ms-excel', // .xls 'application/vnd.ms-excel.sheet.macroEnabled.12', // .xlsm 'text/csv', 'text/plain' ]; if (!allowedTypes.includes(file.type) && !file.name.match(/\.(xlsx|xls|xlsm|csv|txt)$/i)) { alert('지원 형식: 엑셀(.xlsx, .xls, .xlsm), CSV, 텍스트 파일만 업로드 가능합니다.'); return; } setUploadStatus('uploading'); setUploadProgress(0); setAnalysisStep('파일 업로드 중...'); try { const formData = new FormData(); formData.append('file', file); formData.append('project_id', selectedProject); formData.append('revision', 'Rev.0'); formData.append('description', `${file.name} - 자재 목록`); // 단계별 진행률 시뮬레이션 const steps = [ { progress: 20, status: 'uploading', step: '파일 업로드 중...' }, { progress: 40, status: 'analyzing', step: '자동 구조 인식 중... (컬럼 분석)' }, { progress: 60, status: 'classifying', step: '4단계 자동 분류 진행 중...' }, { progress: 80, status: 'classifying', step: '재질 코드 및 사이즈 표준화...' }, { progress: 90, status: 'classifying', step: 'DB 저장 중...' } ]; let stepIndex = 0; const progressInterval = setInterval(() => { if (stepIndex < steps.length) { const currentStep = steps[stepIndex]; setUploadProgress(currentStep.progress); setUploadStatus(currentStep.status); setAnalysisStep(currentStep.step); stepIndex++; } else { clearInterval(progressInterval); } }, 800); const response = await fetch('http://localhost:8000/api/files/upload', { method: 'POST', body: formData, }); clearInterval(progressInterval); setUploadProgress(100); if (response.ok) { const result = await response.json(); setUploadResult(result); setUploadStatus('success'); setAnalysisStep('분류 완료! 결과를 확인하세요.'); // 분류 결과 미리보기 생성 setClassificationPreview({ totalItems: result.parsed_count || 0, categories: { '파이프': Math.floor((result.parsed_count || 0) * 0.4), '피팅류': Math.floor((result.parsed_count || 0) * 0.3), '볼트(너트)': Math.floor((result.parsed_count || 0) * 0.15), '밸브': Math.floor((result.parsed_count || 0) * 0.1), '계기류': Math.floor((result.parsed_count || 0) * 0.05) }, materials: ['A333-6 (저온용 배관)', 'A105 (단조 탄소강)', 'S355', 'SM490'], sizes: ['1"', '2"', '3"', '4"', '6"', '8"'] }); } else { throw new Error('업로드 실패'); } } catch (error) { console.error('Upload error:', error); setUploadStatus('error'); setAnalysisStep('처리 중 오류가 발생했습니다.'); setTimeout(() => { setUploadStatus('idle'); setUploadProgress(0); setAnalysisStep(''); }, 3000); } }; const handleFileInput = (e) => { if (e.target.files && e.target.files[0]) { handleFileUpload(e.target.files[0]); } }; const resetUpload = () => { setUploadStatus('idle'); setUploadProgress(0); setUploadResult(null); setAnalysisStep(''); setClassificationPreview(null); }; return (
Phase 1: 파일 분석 → 4단계 자동 분류 → 체계적 DB 저장
드래그 앤 드롭하거나 클릭하여 파일을 선택하세요
{uploadProgress}% 완료
{analysisStep}
{analysisStep}
파이프 / 피팅류 / 볼트(너트) / 밸브 / 계기류
90도 엘보우 / 용접목 플랜지 / SEAMLESS 파이프
A333-6 (저온용 배관) / A105 (단조 탄소강) / S355 / SM490
6.0" → 6인치, 규격 통일 및 단위 자동 결정