Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
- AuthContext를 통한 통합 인증 상태 관리로 변경 - App.jsx와 AuthContext 간 중복 사용자 상태 관리 제거 - SimpleLogin에서 AuthContext의 login 함수 직접 사용 - 로그인 후 사용자 역할이 즉시 올바르게 인식되도록 개선 - 새로고침 없이도 시스템 관리자 권한 정상 표시 변경사항: - App.jsx: AuthProvider 래퍼 추가, 중복 인증 로직 제거 - SimpleLogin.jsx: AuthContext 직접 사용으로 변경 - 사용자 상태 동기화 문제 완전 해결
412 lines
14 KiB
JavaScript
412 lines
14 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
|
import SimpleLogin from './SimpleLogin';
|
|
import DashboardPage from './pages/DashboardPage';
|
|
import { UserMenu, ErrorBoundary } from './components/common';
|
|
import NewMaterialsPage from './pages/NewMaterialsPage';
|
|
import BOMManagementPage from './pages/BOMManagementPage';
|
|
import UnifiedBOMPage from './pages/UnifiedBOMPage';
|
|
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 InactiveProjectsPage from './pages/InactiveProjectsPage';
|
|
import { AuthProvider, useAuth } from './contexts/AuthContext';
|
|
import errorLogger from './utils/errorLogger';
|
|
import api from './api';
|
|
import './App.css';
|
|
|
|
function AppContent() {
|
|
// TK-MP BOM Management System v2.0 - Project Selection Dashboard
|
|
const { user, isAuthenticated, isLoading, logout } = useAuth();
|
|
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 [inactiveProjects, setInactiveProjects] = useState(() => {
|
|
// localStorage에서 비활성화된 프로젝트 목록 로드
|
|
try {
|
|
const saved = localStorage.getItem('inactiveProjects');
|
|
return saved ? new Set(JSON.parse(saved)) : new Set();
|
|
} catch (error) {
|
|
console.error('비활성화 프로젝트 목록 로드 실패:', error);
|
|
return new Set();
|
|
}
|
|
});
|
|
|
|
// 비활성화 프로젝트 목록이 변경될 때마다 localStorage에 저장
|
|
useEffect(() => {
|
|
try {
|
|
localStorage.setItem('inactiveProjects', JSON.stringify(Array.from(inactiveProjects)));
|
|
} catch (error) {
|
|
console.error('비활성화 프로젝트 목록 저장 실패:', error);
|
|
}
|
|
}, [inactiveProjects]);
|
|
|
|
// 승인 대기 중인 회원가입 수 조회
|
|
const loadPendingSignups = async () => {
|
|
try {
|
|
const response = await api.get('/auth/pending-signups/count');
|
|
setPendingSignupCount(response.data.count || 0);
|
|
} catch (error) {
|
|
console.error('승인 대기 회원가입 수 조회 실패:', error);
|
|
setPendingSignupCount(0);
|
|
}
|
|
};
|
|
|
|
// 프로젝트 목록 로드
|
|
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);
|
|
const errorMsg = error.response?.data?.detail || '프로젝트 수정에 실패했습니다.';
|
|
alert(errorMsg);
|
|
}
|
|
};
|
|
|
|
// 프로젝트 삭제
|
|
const deleteProject = async (projectId) => {
|
|
if (window.confirm('정말로 이 프로젝트를 삭제하시겠습니까?')) {
|
|
try {
|
|
const response = await api.delete(`/dashboard/projects/${projectId}`);
|
|
if (response.data.success) {
|
|
alert('프로젝트가 삭제되었습니다.');
|
|
await loadProjects();
|
|
// 비활성 프로젝트 목록에서도 제거
|
|
setInactiveProjects(prev => {
|
|
const newSet = new Set(prev);
|
|
newSet.delete(projectId);
|
|
return newSet;
|
|
});
|
|
if (selectedProject?.id === projectId) {
|
|
setSelectedProject(null);
|
|
}
|
|
} else {
|
|
alert(`프로젝트 삭제 실패: ${response.data.detail}`);
|
|
}
|
|
} catch (error) {
|
|
console.error('프로젝트 삭제 실패:', error);
|
|
alert(`프로젝트 삭제 실패: ${error.response?.data?.detail || error.message}`);
|
|
}
|
|
}
|
|
};
|
|
|
|
// 프로젝트 활성화
|
|
const handleActivateProject = (project) => {
|
|
const projectId = project.job_no || project.official_project_code || project.id;
|
|
console.log('🔄 프로젝트 활성화:', { project, projectId });
|
|
|
|
setInactiveProjects(prev => {
|
|
const newSet = new Set(prev);
|
|
newSet.delete(projectId);
|
|
console.log('📦 활성화 프로젝트 업데이트:', { prev: Array.from(prev), new: Array.from(newSet) });
|
|
return newSet;
|
|
});
|
|
};
|
|
|
|
// 인증된 사용자의 데이터 로드
|
|
useEffect(() => {
|
|
if (isAuthenticated && user) {
|
|
loadPendingSignups();
|
|
loadProjects();
|
|
}
|
|
}, [isAuthenticated, user]);
|
|
|
|
// 페이지 이동 함수
|
|
const navigateToPage = (page, params = {}) => {
|
|
setCurrentPage(page);
|
|
setPageParams(params);
|
|
setShowUserMenu(false);
|
|
};
|
|
|
|
// 페이지 렌더링 함수
|
|
const renderCurrentPage = () => {
|
|
switch (currentPage) {
|
|
case 'dashboard':
|
|
return (
|
|
<DashboardPage
|
|
user={user}
|
|
projects={projects}
|
|
pendingSignupCount={pendingSignupCount}
|
|
navigateToPage={navigateToPage}
|
|
loadProjects={loadProjects}
|
|
createProject={createProject}
|
|
updateProjectName={updateProjectName}
|
|
deleteProject={deleteProject}
|
|
editingProject={editingProject}
|
|
setEditingProject={setEditingProject}
|
|
editedProjectName={editedProjectName}
|
|
setEditedProjectName={setEditedProjectName}
|
|
showCreateProject={showCreateProject}
|
|
setShowCreateProject={setShowCreateProject}
|
|
newProjectCode={newProjectCode}
|
|
setNewProjectCode={setNewProjectCode}
|
|
newProjectName={newProjectName}
|
|
setNewProjectName={setNewProjectName}
|
|
newClientName={newClientName}
|
|
setNewClientName={setNewClientName}
|
|
inactiveProjects={inactiveProjects}
|
|
setInactiveProjects={setInactiveProjects}
|
|
/>
|
|
);
|
|
|
|
case 'unified-bom':
|
|
return (
|
|
<UnifiedBOMPage
|
|
onNavigate={navigateToPage}
|
|
selectedProject={pageParams.selectedProject}
|
|
user={user}
|
|
/>
|
|
);
|
|
case 'materials':
|
|
return (
|
|
<BOMManagementPage
|
|
onNavigate={navigateToPage}
|
|
user={user}
|
|
selectedProject={pageParams.selectedProject}
|
|
fileId={pageParams.file_id}
|
|
jobNo={pageParams.jobNo}
|
|
bomName={pageParams.bomName}
|
|
revision={pageParams.revision}
|
|
filename={pageParams.filename}
|
|
/>
|
|
);
|
|
case 'purchase-batch':
|
|
return (
|
|
<PurchaseBatchPage
|
|
onNavigate={navigateToPage}
|
|
user={user}
|
|
selectedProject={pageParams.selectedProject}
|
|
/>
|
|
);
|
|
case 'purchase-request':
|
|
return (
|
|
<PurchaseRequestPage
|
|
onNavigate={navigateToPage}
|
|
user={user}
|
|
selectedProject={pageParams.selectedProject}
|
|
/>
|
|
);
|
|
case 'system-settings':
|
|
return (
|
|
<SystemSettingsPage
|
|
onNavigate={navigateToPage}
|
|
user={user}
|
|
/>
|
|
);
|
|
case 'account-settings':
|
|
return (
|
|
<AccountSettingsPage
|
|
onNavigate={navigateToPage}
|
|
user={user}
|
|
onUserUpdate={(updatedUser) => {
|
|
setUser(updatedUser);
|
|
localStorage.setItem('user_data', JSON.stringify(updatedUser));
|
|
}}
|
|
/>
|
|
);
|
|
case 'user-management':
|
|
return (
|
|
<UserManagementPage
|
|
onNavigate={navigateToPage}
|
|
user={user}
|
|
/>
|
|
);
|
|
case 'system-logs':
|
|
return (
|
|
<SystemLogsPage
|
|
onNavigate={navigateToPage}
|
|
user={user}
|
|
/>
|
|
);
|
|
case 'log-monitoring':
|
|
return (
|
|
<LogMonitoringPage
|
|
onNavigate={navigateToPage}
|
|
user={user}
|
|
/>
|
|
);
|
|
case 'inactive-projects':
|
|
return (
|
|
<InactiveProjectsPage
|
|
onNavigate={navigateToPage}
|
|
user={user}
|
|
projects={projects}
|
|
inactiveProjects={inactiveProjects}
|
|
onActivateProject={handleActivateProject}
|
|
onDeleteProject={deleteProject}
|
|
/>
|
|
);
|
|
default:
|
|
return (
|
|
<div style={{
|
|
display: 'flex',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
height: '100vh',
|
|
background: '#f7fafc'
|
|
}}>
|
|
<div style={{ textAlign: 'center' }}>
|
|
<div style={{ fontSize: '24px', marginBottom: '16px' }}>❓</div>
|
|
<div style={{ fontSize: '16px', color: '#718096' }}>알 수 없는 페이지입니다.</div>
|
|
<button
|
|
onClick={() => navigateToPage('dashboard')}
|
|
style={{
|
|
background: '#4299e1',
|
|
color: 'white',
|
|
border: 'none',
|
|
borderRadius: '6px',
|
|
padding: '12px 24px',
|
|
cursor: 'pointer',
|
|
marginTop: '16px'
|
|
}}
|
|
>
|
|
대시보드로 돌아가기
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
};
|
|
|
|
const handleLogout = async () => {
|
|
await logout();
|
|
setCurrentPage('dashboard');
|
|
};
|
|
|
|
|
|
return (
|
|
<ErrorBoundary errorContext={{ user, currentPage, pageParams }}>
|
|
{isLoading ? (
|
|
<div style={{
|
|
display: 'flex',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
height: '100vh',
|
|
background: '#f7fafc'
|
|
}}>
|
|
<div style={{ textAlign: 'center' }}>
|
|
<div style={{ fontSize: '24px', marginBottom: '16px' }}>🔄</div>
|
|
<div style={{ fontSize: '16px', color: '#718096' }}>로딩 중...</div>
|
|
</div>
|
|
</div>
|
|
) : !isAuthenticated ? (
|
|
<SimpleLogin />
|
|
) : (
|
|
<div style={{ minHeight: '100vh', background: '#f7fafc' }}>
|
|
{/* 상단 헤더 */}
|
|
<div style={{
|
|
background: 'white',
|
|
borderBottom: '1px solid #e2e8f0',
|
|
padding: '16px 32px',
|
|
display: 'flex',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center'
|
|
}}>
|
|
<div>
|
|
<h1 style={{ fontSize: '24px', fontWeight: '700', color: '#2d3748', margin: 0 }}>
|
|
TK-MP BOM Management System
|
|
</h1>
|
|
<p style={{ color: '#718096', fontSize: '14px', margin: '4px 0 0 0' }}>
|
|
{user?.name || user?.username}님 환영합니다
|
|
</p>
|
|
</div>
|
|
|
|
{/* 사용자 메뉴 */}
|
|
<UserMenu
|
|
user={user}
|
|
onNavigate={navigateToPage}
|
|
onLogout={handleLogout}
|
|
/>
|
|
</div>
|
|
|
|
{/* 페이지 컨텐츠 */}
|
|
{renderCurrentPage()}
|
|
</div>
|
|
)}
|
|
</ErrorBoundary>
|
|
);
|
|
}
|
|
|
|
// AuthProvider로 감싸는 래퍼 컴포넌트
|
|
function App() {
|
|
return (
|
|
<AuthProvider>
|
|
<AppContent />
|
|
</AuthProvider>
|
|
);
|
|
}
|
|
|
|
export default App; |