feat: 구매신청 기능 완성 및 SUPPORT/SPECIAL 카테고리 개선
- 모든 카테고리 구매신청 기능 완성 (PIPE, FITTING, VALVE, FLANGE, GASKET, BOLT, SUPPORT, SPECIAL, UNKNOWN) - 구매신청 완료 항목: 회색 배경, 체크박스 비활성화, '구매신청완료' 배지 표시 - 전체 선택/구매신청 시 이미 구매신청된 항목 자동 제외 - 구매신청 quantity 타입 에러 수정 (문자열 -> 정수 변환) SUPPORT 카테고리 (구 U-BOLT): - U-BOLT -> SUPPORT로 카테고리명 변경 - 클램프, 유볼트, 우레탄블럭슈 분류 개선 - 테이블 헤더: 선택-종류-타입-크기-디스크립션-추가요구-사용자요구-수량 - 크기 정보 main_nom 필드에서 가져오기 (배관 인치) - 엑셀 내보내기 형식 조정 SPECIAL 카테고리: - SPECIAL 키워드 자재 자동 분류 (SPECIFICATION 제외) - 파일 업로드 시 SPECIAL 카테고리 처리 로직 추가 - 도면번호 필드 추가 (drawing_name, line_no) - 타입 필드: 크기/스케줄/재질 제외한 핵심 정보 표시 - 엑셀 DWG_NAME, LINE_NUM 컬럼 파싱 및 저장 FITTING 카테고리: - 테이블 컬럼 너비 조정 (선택 2%, 종류 8.5%, 수량 12%) 구매신청 관리: - 엑셀 재다운로드 형식 개선 (BOM 페이지와 동일한 형식) - 그룹화된 자재 정보 포함하여 저장 및 다운로드
This commit is contained in:
@@ -5,6 +5,8 @@ import NewMaterialsPage from './pages/NewMaterialsPage';
|
||||
import SystemSettingsPage from './pages/SystemSettingsPage';
|
||||
import AccountSettingsPage from './pages/AccountSettingsPage';
|
||||
import UserManagementPage from './pages/UserManagementPage';
|
||||
import PurchaseBatchPage from './pages/PurchaseBatchPage';
|
||||
import PurchaseRequestPage from './pages/PurchaseRequestPage';
|
||||
import SystemLogsPage from './pages/SystemLogsPage';
|
||||
import LogMonitoringPage from './pages/LogMonitoringPage';
|
||||
import ErrorBoundary from './components/ErrorBoundary';
|
||||
@@ -27,6 +29,20 @@ function App() {
|
||||
const [newProjectCode, setNewProjectCode] = useState('');
|
||||
const [newProjectName, setNewProjectName] = useState('');
|
||||
const [newClientName, setNewClientName] = useState('');
|
||||
const [pendingSignupCount, setPendingSignupCount] = useState(0);
|
||||
|
||||
// 승인 대기 중인 회원가입 수 조회
|
||||
const loadPendingSignups = async () => {
|
||||
try {
|
||||
const response = await api.get('/auth/signup-requests');
|
||||
// API 응답이 { requests: [...], count: ... } 형태
|
||||
const pendingCount = response.data.count || 0;
|
||||
setPendingSignupCount(pendingCount);
|
||||
} catch (error) {
|
||||
console.error('승인 대기 조회 실패:', error);
|
||||
setPendingSignupCount(0);
|
||||
}
|
||||
};
|
||||
|
||||
// 프로젝트 목록 로드
|
||||
const loadProjects = async () => {
|
||||
@@ -143,11 +159,32 @@ function App() {
|
||||
};
|
||||
}, [showUserMenu]);
|
||||
|
||||
// 관리자인 경우 주기적으로 승인 대기 수 확인
|
||||
useEffect(() => {
|
||||
if ((user?.role === 'admin' || user?.role === 'system') && isAuthenticated) {
|
||||
// 초기 로드
|
||||
loadPendingSignups();
|
||||
|
||||
// 30초마다 확인
|
||||
const interval = setInterval(() => {
|
||||
loadPendingSignups();
|
||||
}, 30000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}
|
||||
}, [user?.role, isAuthenticated]);
|
||||
|
||||
// 로그인 성공 시 호출될 함수
|
||||
const handleLoginSuccess = () => {
|
||||
const userData = localStorage.getItem('user_data');
|
||||
if (userData) {
|
||||
setUser(JSON.parse(userData));
|
||||
const parsedUser = JSON.parse(userData);
|
||||
setUser(parsedUser);
|
||||
|
||||
// 관리자인 경우 승인 대기 수 확인
|
||||
if (parsedUser?.role === 'admin' || parsedUser?.role === 'system') {
|
||||
loadPendingSignups();
|
||||
}
|
||||
}
|
||||
setIsAuthenticated(true);
|
||||
};
|
||||
@@ -173,8 +210,14 @@ function App() {
|
||||
{
|
||||
id: 'bom',
|
||||
title: '📋 BOM 업로드 & 분류',
|
||||
description: '엑셀 파일 업로드 → 자동 분류 → 검토 → 자재 확인 → 엑셀 내보내기',
|
||||
description: '엑셀 파일 업로드 → 자동 분류 → 검토 → 자재 확인 → 구매신청 (엑셀 내보내기)',
|
||||
color: '#4299e1'
|
||||
},
|
||||
{
|
||||
id: 'purchase-request',
|
||||
title: '📦 구매신청 관리',
|
||||
description: '구매신청한 자재들을 그룹별로 조회하고 엑셀 재다운로드',
|
||||
color: '#10b981'
|
||||
}
|
||||
];
|
||||
};
|
||||
@@ -183,15 +226,20 @@ function App() {
|
||||
const getAdminFeatures = () => {
|
||||
const features = [];
|
||||
|
||||
// 시스템 관리자 전용 기능
|
||||
if (user?.role === 'system') {
|
||||
console.log('getAdminFeatures - Current user:', user);
|
||||
console.log('getAdminFeatures - User role:', user?.role);
|
||||
console.log('getAdminFeatures - Pending count:', pendingSignupCount);
|
||||
|
||||
// 시스템 관리자 기능 (admin role이 시스템 관리자)
|
||||
if (user?.role === 'admin') {
|
||||
features.push(
|
||||
{
|
||||
id: 'user-management',
|
||||
title: '👥 사용자 관리',
|
||||
description: '계정 생성, 역할 변경, 사용자 삭제',
|
||||
description: '계정 생성, 역할 변경, 회원가입 승인',
|
||||
color: '#dc2626',
|
||||
badge: '시스템 관리자'
|
||||
badge: '시스템 관리자',
|
||||
pendingCount: pendingSignupCount
|
||||
},
|
||||
{
|
||||
id: 'system-logs',
|
||||
@@ -203,8 +251,24 @@ function App() {
|
||||
);
|
||||
}
|
||||
|
||||
// 일반 관리자 기능
|
||||
if (user?.role === 'manager') {
|
||||
// 일반 관리자는 회원가입 승인만 가능
|
||||
features.push(
|
||||
{
|
||||
id: 'user-management',
|
||||
title: '👥 회원가입 승인',
|
||||
description: '신규 회원가입 승인 및 거부',
|
||||
color: '#dc2626',
|
||||
badge: '관리자',
|
||||
pendingCount: pendingSignupCount
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 관리자 이상 공통 기능
|
||||
if (user?.role === 'admin' || user?.role === 'system') {
|
||||
if (user?.role === 'admin' || user?.role === 'manager') {
|
||||
|
||||
features.push(
|
||||
{
|
||||
id: 'log-monitoring',
|
||||
@@ -661,13 +725,12 @@ function App() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 핵심 기능 */}
|
||||
{/* 핵심 기능 - 프로젝트 선택 시만 표시 */}
|
||||
{selectedProject && (
|
||||
<>
|
||||
<div style={{ marginBottom: '32px' }}>
|
||||
<h2 style={{ fontSize: '20px', fontWeight: '600', color: '#2d3748', marginBottom: '16px' }}>
|
||||
📋 BOM 관리 워크플로우
|
||||
</h2>
|
||||
<div style={{ marginBottom: '32px' }}>
|
||||
<h2 style={{ fontSize: '20px', fontWeight: '600', color: '#2d3748', marginBottom: '16px' }}>
|
||||
📋 BOM 관리 워크플로우
|
||||
</h2>
|
||||
<div style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
|
||||
@@ -701,7 +764,10 @@ function App() {
|
||||
{feature.description}
|
||||
</p>
|
||||
<button
|
||||
onClick={() => navigateToPage(feature.id, { selectedProject })}
|
||||
onClick={() => navigateToPage(feature.id, {
|
||||
selectedProject,
|
||||
jobNo: selectedProject?.official_project_code
|
||||
})}
|
||||
style={{
|
||||
background: feature.color,
|
||||
color: 'white',
|
||||
@@ -720,8 +786,9 @@ function App() {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)} {/* selectedProject 조건문 닫기 */}
|
||||
|
||||
{/* 관리자 기능 (있는 경우만) */}
|
||||
{/* 관리자 기능 (프로젝트 선택과 무관하게 항상 표시) */}
|
||||
{adminFeatures.length > 0 && (
|
||||
<div style={{ marginBottom: '32px' }}>
|
||||
<h2 style={{ fontSize: '20px', fontWeight: '600', color: '#2d3748', marginBottom: '16px' }}>
|
||||
@@ -753,8 +820,29 @@ function App() {
|
||||
e.currentTarget.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.07)';
|
||||
}}
|
||||
>
|
||||
<h3 style={{ fontSize: '18px', fontWeight: '600', color: '#2d3748', marginBottom: '12px' }}>
|
||||
{feature.title}
|
||||
<h3 style={{
|
||||
fontSize: '18px',
|
||||
fontWeight: '600',
|
||||
color: '#2d3748',
|
||||
marginBottom: '12px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between'
|
||||
}}>
|
||||
<span>{feature.title}</span>
|
||||
{feature.id === 'user-management' && feature.pendingCount > 0 && (
|
||||
<span style={{
|
||||
background: '#ef4444',
|
||||
color: 'white',
|
||||
borderRadius: '12px',
|
||||
padding: '2px 8px',
|
||||
fontSize: '12px',
|
||||
fontWeight: '700',
|
||||
animation: 'pulse 2s ease-in-out infinite'
|
||||
}}>
|
||||
{feature.pendingCount}명 대기
|
||||
</span>
|
||||
)}
|
||||
</h3>
|
||||
<p style={{ color: '#718096', marginBottom: '16px', fontSize: '14px' }}>
|
||||
{feature.description}
|
||||
@@ -853,8 +941,7 @@ function App() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)} {/* selectedProject 조건문 닫기 */}
|
||||
)} {/* adminFeatures 조건문 닫기 */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -880,6 +967,25 @@ function App() {
|
||||
filename={pageParams.filename}
|
||||
/>
|
||||
);
|
||||
|
||||
case 'purchase-batch':
|
||||
return (
|
||||
<PurchaseBatchPage
|
||||
onNavigate={navigateToPage}
|
||||
fileId={pageParams.file_id}
|
||||
jobNo={pageParams.jobNo}
|
||||
/>
|
||||
);
|
||||
|
||||
case 'purchase-request':
|
||||
return (
|
||||
<PurchaseRequestPage
|
||||
onNavigate={navigateToPage}
|
||||
fileId={pageParams.file_id}
|
||||
jobNo={pageParams.jobNo}
|
||||
selectedProject={pageParams.selectedProject}
|
||||
/>
|
||||
);
|
||||
|
||||
case 'system-settings':
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user