import React, { useState, useEffect } from 'react';
import SimpleLogin from './SimpleLogin';
import BOMWorkspacePage from './pages/BOMWorkspacePage';
import NewMaterialsPage from './pages/NewMaterialsPage';
import SystemSettingsPage from './pages/SystemSettingsPage';
import AccountSettingsPage from './pages/AccountSettingsPage';
import UserManagementPage from './pages/UserManagementPage';
import SystemLogsPage from './pages/SystemLogsPage';
import LogMonitoringPage from './pages/LogMonitoringPage';
import ErrorBoundary from './components/ErrorBoundary';
import errorLogger from './utils/errorLogger';
import api from './api';
import './App.css';
function App() {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [user, setUser] = useState(null);
const [currentPage, setCurrentPage] = useState('dashboard');
const [pageParams, setPageParams] = useState({});
const [selectedProject, setSelectedProject] = useState(null);
const [showUserMenu, setShowUserMenu] = useState(false);
const [projects, setProjects] = useState([]);
const [editingProject, setEditingProject] = useState(null);
const [editedProjectName, setEditedProjectName] = useState('');
const [showCreateProject, setShowCreateProject] = useState(false);
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');
if (response.data && response.data.requests) {
setPendingSignupCount(response.data.count || 0);
}
} catch (error) {
// 에러 무시 (관리자가 아니면 403)
}
};
// 프로젝트 목록 로드
const loadProjects = async () => {
try {
const response = await api.get('/dashboard/projects');
if (response.data && response.data.projects) {
setProjects(response.data.projects);
}
} catch (error) {
console.error('프로젝트 목록 로드 실패:', error);
// API 실패 시 에러를 무시하고 더미 데이터 사용
}
};
// 프로젝트 생성
const createProject = async () => {
if (!newProjectCode || !newProjectName) {
alert('프로젝트 코드와 이름을 입력해주세요.');
return;
}
try {
const response = await api.post(`/dashboard/projects?official_project_code=${encodeURIComponent(newProjectCode)}&project_name=${encodeURIComponent(newProjectName)}&client_name=${encodeURIComponent(newClientName)}`);
if (response.data.success) {
// 프로젝트 목록 갱신
await loadProjects();
// 폼 초기화
setShowCreateProject(false);
setNewProjectCode('');
setNewProjectName('');
setNewClientName('');
alert('프로젝트가 생성되었습니다.');
}
} catch (error) {
console.error('프로젝트 생성 실패:', error);
const errorMsg = error.response?.data?.detail || '프로젝트 생성에 실패했습니다.';
alert(errorMsg);
}
};
// 프로젝트 이름 수정
const updateProjectName = async (projectId) => {
try {
const response = await api.patch(`/dashboard/projects/${projectId}?job_name=${encodeURIComponent(editedProjectName)}`);
if (response.data.success) {
// 프로젝트 목록 갱신
await loadProjects();
// 선택된 프로젝트 업데이트
if (selectedProject && selectedProject.id === projectId) {
setSelectedProject({
...selectedProject,
project_name: editedProjectName
});
}
setEditingProject(null);
setEditedProjectName('');
alert('프로젝트 이름이 수정되었습니다.');
}
} catch (error) {
console.error('프로젝트 이름 수정 실패:', error);
alert('프로젝트 이름 수정에 실패했습니다.');
}
};
useEffect(() => {
// 저장된 토큰 확인
const token = localStorage.getItem('access_token');
const userData = localStorage.getItem('user_data');
if (token && userData) {
setIsAuthenticated(true);
const userObj = JSON.parse(userData);
setUser(userObj);
loadProjects(); // 프로젝트 목록 로드
// 관리자인 경우 승인 대기 수 조회
if (userObj.role === 'admin' || userObj.role === 'system') {
loadPendingSignups();
// 30초마다 갱신
const interval = setInterval(loadPendingSignups, 30000);
return () => clearInterval(interval);
}
}
setIsLoading(false);
// 자재 목록 페이지로 이동 이벤트 리스너
const handleNavigateToMaterials = (event) => {
const { jobNo, revision, bomName, message, file_id } = event.detail;
navigateToPage('materials', {
jobNo: jobNo,
revision: revision,
bomName: bomName,
message: message,
file_id: file_id // file_id 추가
});
};
window.addEventListener('navigateToMaterials', handleNavigateToMaterials);
return () => {
window.removeEventListener('navigateToMaterials', handleNavigateToMaterials);
};
}, []);
// 사용자 메뉴 외부 클릭 시 닫기
useEffect(() => {
const handleClickOutside = (event) => {
if (showUserMenu && !event.target.closest('.user-menu-container')) {
setShowUserMenu(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [showUserMenu]);
// 로그인 성공 시 호출될 함수
const handleLoginSuccess = () => {
const userData = localStorage.getItem('user_data');
if (userData) {
setUser(JSON.parse(userData));
}
setIsAuthenticated(true);
};
// 로그아웃 함수
const handleLogout = () => {
localStorage.removeItem('access_token');
localStorage.removeItem('user_data');
setIsAuthenticated(false);
setUser(null);
setCurrentPage('dashboard');
};
// 페이지 네비게이션 함수
const navigateToPage = (page, params = {}) => {
setCurrentPage(page);
setPageParams(params);
};
// 핵심 기능만 제공
const getCoreFeatures = () => {
return [
{
id: 'bom',
title: '📋 BOM 업로드 & 분류',
description: '엑셀 파일 업로드 → 자동 분류 → 검토 → 자재 확인 → 엑셀 내보내기',
color: '#4299e1'
}
];
};
// 관리자 전용 기능
const getAdminFeatures = () => {
const features = [];
// 시스템 관리자 전용 기능
if (user?.role === 'system') {
features.push(
{
id: 'user-management',
title: '👥 사용자 관리',
description: '계정 생성, 역할 변경, 사용자 삭제',
color: '#dc2626',
badge: '시스템 관리자'
},
{
id: 'system-logs',
title: '📊 시스템 로그',
description: '로그인 기록, 시스템 오류 로그 조회',
color: '#7c3aed',
badge: '시스템 관리자'
}
);
}
// 관리자 이상 공통 기능
if (user?.role === 'admin' || user?.role === 'system') {
features.push(
{
id: 'log-monitoring',
title: '📈 로그 모니터링',
description: '사용자 활동 로그 및 오류 모니터링',
color: '#059669',
badge: user?.role === 'system' ? '시스템 관리자' : '관리자'
}
);
}
return features;
};
// 페이지 렌더링 함수
const renderCurrentPage = () => {
console.log('현재 페이지:', currentPage, '페이지 파라미터:', pageParams);
switch (currentPage) {
case 'dashboard':
const coreFeatures = getCoreFeatures();
const adminFeatures = getAdminFeatures();
return (
{/* 상단 헤더 */}
🏭 TK-MP BOM 관리 시스템
{user?.name || user?.username}님 환영합니다
{/* 알람 및 사용자 메뉴 */}
{/* 회원가입 승인 알람 (관리자 전용) */}
{(user?.role === 'admin' || user?.role === 'system') && pendingSignupCount > 0 && (
)}
{/* 사용자 메뉴 */}
{/* 드롭다운 메뉴 */}
{showUserMenu && (
{user?.name || user?.username}
{user?.email || '이메일 없음'}
)}
{/* 메인 콘텐츠 */}
{/* 프로젝트 관리 */}
📁 프로젝트 관리
{selectedProject && (
)}
{/* 프로젝트 생성 폼 */}
{showCreateProject && (
➕ 새 프로젝트 생성
setNewProjectCode(e.target.value)}
placeholder="예: J24-004"
style={{
width: '100%',
padding: '10px 12px',
border: '1px solid #d1d5db',
borderRadius: '6px',
fontSize: '14px'
}}
/>
setNewProjectName(e.target.value)}
placeholder="예: 새로운 프로젝트"
style={{
width: '100%',
padding: '10px 12px',
border: '1px solid #d1d5db',
borderRadius: '6px',
fontSize: '14px'
}}
/>
setNewClientName(e.target.value)}
placeholder="예: ABC 주식회사"
style={{
width: '100%',
padding: '10px 12px',
border: '1px solid #d1d5db',
borderRadius: '6px',
fontSize: '14px'
}}
/>
)}
{/* 프로젝트 이름 편집 폼 */}
{editingProject && (
프로젝트 이름 수정: {editingProject.official_project_code}
setEditedProjectName(e.target.value)}
onKeyPress={(e) => {
if (e.key === 'Enter') updateProjectName(editingProject.id);
}}
placeholder="새 프로젝트 이름"
autoFocus
style={{
flex: 1,
padding: '10px 12px',
border: '2px solid #3b82f6',
borderRadius: '6px',
fontSize: '14px'
}}
/>
)}
{/* 핵심 기능 */}
{selectedProject && (
<>
📋 BOM 관리 워크플로우
{coreFeatures.map((feature) => (
{
e.currentTarget.style.transform = 'translateY(-2px)';
e.currentTarget.style.boxShadow = '0 8px 12px rgba(0, 0, 0, 0.1)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.transform = 'translateY(0)';
e.currentTarget.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.07)';
}}
>
{feature.title}
{feature.description}
))}
{/* 관리자 기능 (있는 경우만) */}
{adminFeatures.length > 0 && (
⚙️ 시스템 관리
{adminFeatures.map((feature) => (
{
e.currentTarget.style.transform = 'translateY(-2px)';
e.currentTarget.style.boxShadow = '0 8px 12px rgba(0, 0, 0, 0.1)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.transform = 'translateY(0)';
e.currentTarget.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.07)';
}}
>
{feature.title}
{feature.description}
{feature.badge} 전용
))}
)}
{/* 간단한 사용법 안내 */}
📖 간단한 사용법
1
BOM 업로드
→
2
자동 분류
→
3
엑셀 내보내기
>
)} {/* selectedProject 조건문 닫기 */}
);
case 'bom':
return (
navigateToPage('dashboard')}
/>
);
case 'materials':
return (
);
case 'system-settings':
return (
);
case 'account-settings':
return (
{
setUser(updatedUser);
localStorage.setItem('user_data', JSON.stringify(updatedUser));
}}
/>
);
case 'user-management':
return (
);
case 'system-logs':
return (
);
case 'log-monitoring':
return (
);
default:
return (
페이지를 찾을 수 없습니다
);
}
};
// 로딩 중
if (isLoading) {
return (
);
}
// 로그인하지 않은 경우
if (!isAuthenticated) {
return ;
}
// 메인 애플리케이션
return (
{renderCurrentPage()}
);
}
export default App;