feat: 자재 리비전 비교 및 구매 목록 시스템 구현
- 자재 리비전간 비교 기능 추가 (MaterialComparisonPage) - 버그 해결 필요 - 리비전간 추가 구매 필요 자재 분석 페이지 추가 (RevisionPurchasePage) - 자재 비교 결과 컴포넌트 구현 (MaterialComparisonResult) - 자재 비교 API 라우터 추가 (material_comparison.py) - 로직 개선 필요 - 자재 비교 시스템 데이터베이스 스키마 추가 - FileManager, FileUpload 컴포넌트 개선 - BOMManagerPage 제거 및 새로운 구조로 리팩토링 - 자재 분류기 및 스키마 개선 TODO: 자재 비교 알고리즘 정확도 향상 및 예외 처리 강화 필요
This commit is contained in:
@@ -16,11 +16,17 @@ import {
|
||||
Alert,
|
||||
CircularProgress,
|
||||
Chip,
|
||||
Divider
|
||||
Divider,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
Select,
|
||||
MenuItem,
|
||||
Grid
|
||||
} from '@mui/material';
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||
import ShoppingCart from '@mui/icons-material/ShoppingCart';
|
||||
import { api } from '../api';
|
||||
import { Compare as CompareIcon } from '@mui/icons-material';
|
||||
import { api, fetchFiles } from '../api';
|
||||
|
||||
const MaterialsPage = () => {
|
||||
const [materials, setMaterials] = useState([]);
|
||||
@@ -28,23 +34,63 @@ const MaterialsPage = () => {
|
||||
const [error, setError] = useState(null);
|
||||
const [fileId, setFileId] = useState(null);
|
||||
const [fileName, setFileName] = useState('');
|
||||
const [jobNo, setJobNo] = useState('');
|
||||
const [bomName, setBomName] = useState('');
|
||||
const [currentRevision, setCurrentRevision] = useState('');
|
||||
const [availableRevisions, setAvailableRevisions] = useState([]);
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const id = urlParams.get('file_id');
|
||||
const name = urlParams.get('filename') || '';
|
||||
const job_no = urlParams.get('job_no') || '';
|
||||
|
||||
if (id) {
|
||||
if (id && job_no) {
|
||||
setFileId(id);
|
||||
setFileName(decodeURIComponent(name));
|
||||
setJobNo(job_no);
|
||||
loadMaterials(id);
|
||||
loadAvailableRevisions(job_no, name);
|
||||
} else {
|
||||
setLoading(false);
|
||||
setError('파일 ID가 지정되지 않았습니다.');
|
||||
setError('파일 ID 또는 Job No가 지정되지 않았습니다.');
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 같은 BOM의 다른 리비전들 로드
|
||||
const loadAvailableRevisions = async (job_no, filename) => {
|
||||
try {
|
||||
const response = await fetchFiles({ job_no });
|
||||
if (Array.isArray(response.data)) {
|
||||
// 같은 BOM 이름의 파일들만 필터링
|
||||
const sameNameFiles = response.data.filter(file =>
|
||||
file.original_filename === filename ||
|
||||
file.bom_name === filename ||
|
||||
file.filename === filename
|
||||
);
|
||||
|
||||
// 리비전 순으로 정렬 (최신부터)
|
||||
const sortedFiles = sameNameFiles.sort((a, b) => {
|
||||
const revA = parseInt(a.revision?.replace('Rev.', '') || '0');
|
||||
const revB = parseInt(b.revision?.replace('Rev.', '') || '0');
|
||||
return revB - revA;
|
||||
});
|
||||
|
||||
setAvailableRevisions(sortedFiles);
|
||||
|
||||
// 현재 파일 정보 설정
|
||||
const currentFile = sortedFiles.find(file => file.id === parseInt(fileId));
|
||||
if (currentFile) {
|
||||
setCurrentRevision(currentFile.revision || 'Rev.0');
|
||||
setBomName(currentFile.bom_name || currentFile.original_filename);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('리비전 목록 로드 실패:', err);
|
||||
}
|
||||
};
|
||||
|
||||
const loadMaterials = async (id) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
@@ -586,21 +632,75 @@ const MaterialsPage = () => {
|
||||
뒤로가기
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
color="success"
|
||||
size="large"
|
||||
startIcon={<ShoppingCart />}
|
||||
onClick={() => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
navigate(`/purchase-confirmation?${params.toString()}`);
|
||||
}}
|
||||
disabled={materialSpecs.length === 0}
|
||||
sx={{ minWidth: 150 }}
|
||||
>
|
||||
구매 확정
|
||||
</Button>
|
||||
<Box sx={{ display: 'flex', gap: 2, alignItems: 'center' }}>
|
||||
{/* 리비전 비교 버튼 */}
|
||||
{availableRevisions.length > 1 && currentRevision !== 'Rev.0' && (
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
startIcon={<CompareIcon />}
|
||||
onClick={() => navigate(`/material-comparison?job_no=${jobNo}&revision=${currentRevision}&filename=${encodeURIComponent(fileName)}`)}
|
||||
>
|
||||
리비전 비교
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
color="success"
|
||||
size="large"
|
||||
startIcon={<ShoppingCart />}
|
||||
onClick={() => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
navigate(`/purchase-confirmation?${params.toString()}`);
|
||||
}}
|
||||
disabled={materialSpecs.length === 0}
|
||||
sx={{ minWidth: 150 }}
|
||||
>
|
||||
구매 확정
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* 리비전 선택 */}
|
||||
{availableRevisions.length > 1 && (
|
||||
<Card sx={{ mb: 3, p: 2 }}>
|
||||
<Grid container spacing={2} alignItems="center">
|
||||
<Grid item xs={12} md={6}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
📋 {bomName}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="textSecondary">
|
||||
Job No: {jobNo} | 현재 리비전: <strong>{currentRevision}</strong>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<FormControl fullWidth size="small">
|
||||
<InputLabel>리비전 선택</InputLabel>
|
||||
<Select
|
||||
value={fileId || ''}
|
||||
label="리비전 선택"
|
||||
onChange={(e) => {
|
||||
const selectedFileId = e.target.value;
|
||||
const selectedFile = availableRevisions.find(file => file.id === selectedFileId);
|
||||
if (selectedFile) {
|
||||
// 새로운 리비전 페이지로 이동
|
||||
navigate(`/materials?file_id=${selectedFileId}&job_no=${jobNo}&filename=${encodeURIComponent(selectedFile.original_filename || selectedFile.filename)}`);
|
||||
window.location.reload(); // 페이지 새로고침으로 데이터 갱신
|
||||
}
|
||||
}}
|
||||
>
|
||||
{availableRevisions.map((file) => (
|
||||
<MenuItem key={file.id} value={file.id}>
|
||||
{file.revision || 'Rev.0'} ({file.parsed_count || 0}개 자재) - {new Date(file.upload_date).toLocaleDateString('ko-KR')}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
<Typography variant="h4" component="h1" gutterBottom>
|
||||
📋 자재 사양서
|
||||
|
||||
Reference in New Issue
Block a user