import React, { useState, useEffect } from 'react';
import { fetchMaterials } from '../api';
import api from '../api';
import {
PipeMaterialsView,
FittingMaterialsView,
FlangeMaterialsView,
ValveMaterialsView,
GasketMaterialsView,
BoltMaterialsView,
SupportMaterialsView,
SpecialMaterialsView,
UnclassifiedMaterialsView
} from '../components/bom';
import './BOMManagementPage.css';
const BOMManagementPage = ({
onNavigate,
selectedProject,
fileId,
jobNo,
bomName,
revision,
filename,
user
}) => {
const [materials, setMaterials] = useState([]);
const [loading, setLoading] = useState(true);
const [selectedCategory, setSelectedCategory] = useState('PIPE');
const [selectedMaterials, setSelectedMaterials] = useState(new Set());
const [exportHistory, setExportHistory] = useState([]);
const [availableRevisions, setAvailableRevisions] = useState([]);
const [currentRevision, setCurrentRevision] = useState(revision || 'Rev.0');
const [userRequirements, setUserRequirements] = useState({});
const [purchasedMaterials, setPurchasedMaterials] = useState(new Set());
const [error, setError] = useState(null);
// 리비전 관련 상태
const [isRevisionMode, setIsRevisionMode] = useState(false);
const [revisionData, setRevisionData] = useState(null);
const [previousFileId, setPreviousFileId] = useState(null);
const [changedMaterials, setChangedMaterials] = useState({});
// 자재 업데이트 함수 (브랜드, 사용자 요구사항 등)
const updateMaterial = (materialId, updates) => {
setMaterials(prevMaterials =>
prevMaterials.map(material =>
material.id === materialId
? { ...material, ...updates }
: material
)
);
};
// 카테고리 정의
const categories = [
{ key: 'PIPE', label: 'Pipes', color: '#3b82f6' },
{ key: 'FITTING', label: 'Fittings', color: '#10b981' },
{ key: 'FLANGE', label: 'Flanges', color: '#f59e0b' },
{ key: 'VALVE', label: 'Valves', color: '#ef4444' },
{ key: 'GASKET', label: 'Gaskets', color: '#8b5cf6' },
{ key: 'BOLT', label: 'Bolts', color: '#6b7280' },
{ key: 'SUPPORT', label: 'Supports', color: '#f97316' },
{ key: 'SPECIAL', label: 'Special Items', color: '#ec4899' },
{ key: 'UNCLASSIFIED', label: 'Unclassified', color: '#64748b' }
];
// 자료 로드 함수들
const loadMaterials = async (id) => {
try {
setLoading(true);
console.log('🔍 자재 데이터 로딩 중...', {
file_id: id,
selectedProject: selectedProject?.job_no || selectedProject?.official_project_code,
jobNo
});
// 구매신청된 자재 먼저 확인
const projectJobNo = selectedProject?.job_no || selectedProject?.official_project_code || jobNo;
await loadPurchasedMaterials(projectJobNo);
const response = await fetchMaterials({
file_id: parseInt(id),
limit: 10000,
exclude_requested: false,
job_no: projectJobNo
});
if (response.data?.materials) {
const materialsData = response.data.materials;
console.log(`✅ ${materialsData.length}개 원본 자재 로드 완료`);
setMaterials(materialsData);
setError(null);
} else {
console.warn('⚠️ 자재 데이터가 없습니다:', response.data);
setMaterials([]);
}
} catch (error) {
console.error('자재 로드 실패:', error);
setError('자재 로드에 실패했습니다.');
} finally {
setLoading(false);
}
};
const loadAvailableRevisions = async () => {
try {
const response = await api.get('/files/', {
params: { job_no: jobNo }
});
const allFiles = Array.isArray(response.data) ? response.data : response.data?.files || [];
const sameBomFiles = allFiles.filter(file =>
(file.bom_name || file.original_filename) === bomName
);
sameBomFiles.sort((a, b) => {
const revA = parseInt(a.revision?.replace('Rev.', '') || '0');
const revB = parseInt(b.revision?.replace('Rev.', '') || '0');
return revB - revA;
});
setAvailableRevisions(sameBomFiles);
} catch (error) {
console.error('리비전 목록 조회 실패:', error);
}
};
const loadPurchasedMaterials = async (jobNo) => {
try {
// 새로운 API로 구매신청된 자재 ID 목록 조회
const response = await api.get('/purchase-request/requested-materials', {
params: {
job_no: jobNo,
file_id: fileId
}
});
if (response.data?.requested_material_ids) {
const purchasedIds = new Set(response.data.requested_material_ids);
setPurchasedMaterials(purchasedIds);
console.log(`✅ ${purchasedIds.size}개 구매신청된 자재 ID 로드 완료`);
}
} catch (error) {
console.error('구매신청 자재 조회 실패:', error);
}
};
const loadUserRequirements = async (fileId) => {
try {
const response = await api.get(`/files/${fileId}/user-requirements`);
if (response.data?.requirements) {
const reqMap = {};
response.data.requirements.forEach(req => {
reqMap[req.material_id] = req.requirement;
});
setUserRequirements(reqMap);
}
} catch (error) {
console.error('사용자 요구사항 로드 실패:', error);
}
};
// 리비전 모드 감지 및 변경된 자재 로드
const checkAndLoadRevisionData = async () => {
try {
// 현재 job_no의 모든 파일 목록 확인
const filesResponse = await api.get(`/files/list?job_no=${jobNo}`);
const files = filesResponse.data.files || [];
if (files.length > 1) {
// 파일이 여러 개 있으면 리비전 모드 활성화
setIsRevisionMode(true);
// 파일들을 업로드 날짜순으로 정렬
const sortedFiles = files.sort((a, b) => new Date(a.upload_date) - new Date(b.upload_date));
// 이전 파일 ID 찾기 (현재 파일 이전 버전)
const currentIndex = sortedFiles.findIndex(file => file.id === parseInt(fileId));
if (currentIndex > 0) {
const previousFile = sortedFiles[currentIndex - 1];
setPreviousFileId(previousFile.id);
// 변경된 자재 로드
await loadChangedMaterials(fileId, previousFile.id);
}
}
} catch (error) {
console.error('리비전 데이터 로드 실패:', error);
// API 오류 시 리비전 모드 비활성화
setIsRevisionMode(false);
}
};
// 변경된 자재 로드
const loadChangedMaterials = async (currentFileId, previousFileId) => {
try {
const response = await api.get(`/simple-revision/changed-materials/${currentFileId}/${previousFileId}`);
if (response.data.success) {
setChangedMaterials(response.data.data.changes_by_category || {});
setRevisionData(response.data.data);
console.log('✅ 변경된 자재 로드 완료:', response.data.data);
}
} catch (error) {
console.error('변경된 자재 로드 실패:', error);
}
};
// 초기 로드
useEffect(() => {
if (fileId) {
loadMaterials(fileId);
loadAvailableRevisions();
loadUserRequirements(fileId);
checkAndLoadRevisionData(); // 리비전 데이터 확인
}
}, [fileId]);
// 자재 로드 후 선택된 카테고리가 유효한지 확인
useEffect(() => {
if (materials.length > 0) {
const availableCategories = categories.filter(category => {
const count = getCategoryMaterials(category.key).length;
return count > 0;
});
// 현재 선택된 카테고리에 자재가 없으면 첫 번째 유효한 카테고리로 전환
const currentCategoryHasMaterials = getCategoryMaterials(selectedCategory).length > 0;
if (!currentCategoryHasMaterials && availableCategories.length > 0) {
setSelectedCategory(availableCategories[0].key);
}
}
}, [materials, selectedCategory]);
// 카테고리별 자재 필터링 (리비전 모드 지원)
const getCategoryMaterials = (category) => {
if (isRevisionMode && changedMaterials[category]) {
// 리비전 모드: 변경된 자재만 표시
const changedMaterialIds = changedMaterials[category].changes.map(change => change.material_id);
return materials.filter(material =>
(material.classified_category === category || material.category === category) &&
changedMaterialIds.includes(material.id)
);
} else {
// 일반 모드: 모든 자재 표시
return materials.filter(material =>
material.classified_category === category ||
material.category === category
);
}
};
// 리비전 액션 정보 가져오기
const getRevisionAction = (materialId, category) => {
if (!isRevisionMode || !changedMaterials[category]) return null;
const change = changedMaterials[category].changes.find(c => c.material_id === materialId);
return change || null;
};
// 카테고리별 컴포넌트 렌더링
const renderCategoryView = () => {
const categoryMaterials = getCategoryMaterials(selectedCategory);
const commonProps = {
materials: categoryMaterials,
selectedMaterials,
setSelectedMaterials,
userRequirements,
setUserRequirements,
purchasedMaterials,
onPurchasedMaterialsUpdate: (materialIds) => {
setPurchasedMaterials(prev => {
const newSet = new Set(prev);
materialIds.forEach(id => newSet.add(id));
console.log(`📦 구매신청 자재 추가: 기존 ${prev.size}개 → 신규 ${newSet.size}개`);
return newSet;
});
},
updateMaterial, // 자재 업데이트 함수 추가
fileId,
jobNo,
user,
onNavigate,
// 리비전 관련 props 추가
isRevisionMode,
getRevisionAction: (materialId) => getRevisionAction(materialId, selectedCategory),
revisionData
};
switch (selectedCategory) {
case 'PIPE':
return
{bomName} - {currentRevision} | Project: {selectedProject?.job_name || jobNo} {isRevisionMode && revisionData && ( • {revisionData.total_changed_materials} materials changed )}