Files
tk-factory-services/tkeg/web/src/App.jsx
Hyungi Ahn 1ceeef2a65 refactor(tkeg): print→logging 교체 + 레거시 파일 정리 (-5,447줄)
- 6개 파일 디버그 print문 102건 → logger.info/warning/error 교체
- DashboardPage.old.jsx 삭제 (미사용)
- NewMaterialsPage.jsx/css 삭제 (dead import)
- spool_manager_v2.py 삭제 (미사용)
- App.jsx dead import 제거
- main.py 구 주석 블록 제거

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 15:41:39 +09:00

229 lines
8.2 KiB
JavaScript

import React, { useState, useEffect } from 'react';
import DashboardPage from './pages/dashboard/DashboardPage';
import { UserMenu, ErrorBoundary } from './components/common';
import BOMManagementPage from './pages/BOMManagementPage';
import UnifiedBOMPage from './pages/UnifiedBOMPage';
import SystemSettingsPage from './pages/SystemSettingsPage';
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 api from './api';
import { config } from './config';
import './App.css';
function getSSOToken() {
const match = document.cookie.match(/sso_token=([^;]*)/);
return match ? match[1] : null;
}
function parseJwt(token) {
try {
const base64Url = token.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const jsonPayload = decodeURIComponent(
atob(base64).split('').map(c =>
'%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
).join('')
);
return JSON.parse(jsonPayload);
} catch { return null; }
}
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 [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 [inactiveProjects, setInactiveProjects] = useState(() => {
try {
const saved = localStorage.getItem('tkeg_inactiveProjects');
return saved ? new Set(JSON.parse(saved)) : new Set();
} catch { return new Set(); }
});
useEffect(() => {
try {
localStorage.setItem('tkeg_inactiveProjects', JSON.stringify(Array.from(inactiveProjects)));
} catch {}
}, [inactiveProjects]);
const loadProjects = async () => {
try {
const response = await api.get('/jobs/');
if (response.data?.jobs) {
setProjects(response.data.jobs);
}
} catch (error) {
console.error('프로젝트 목록 로드 실패:', error);
}
};
const handleActivateProject = (project) => {
const projectId = project.job_no || project.id;
setInactiveProjects(prev => {
const newSet = new Set(prev);
newSet.delete(projectId);
return newSet;
});
};
// SSO 인증 확인
useEffect(() => {
const token = getSSOToken();
if (!token) {
window.location.href = config.ssoLoginUrl(window.location.href);
return;
}
const payload = parseJwt(token);
if (!payload) {
window.location.href = config.ssoLoginUrl();
return;
}
setUser({
user_id: payload.user_id,
username: payload.sub || payload.username,
name: payload.name || payload.username || payload.sub,
role: payload.role || 'user',
department: payload.department,
});
setIsAuthenticated(true);
setIsLoading(false);
loadProjects();
}, []);
const navigateToPage = (page, params = {}) => {
setCurrentPage(page);
setPageParams(params);
};
const handleLogout = () => {
document.cookie = `sso_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/${config.cookieDomain}`;
window.location.href = config.ssoLoginUrl();
};
const renderCurrentPage = () => {
switch (currentPage) {
case 'dashboard':
return (
<DashboardPage
user={user}
projects={projects}
pendingSignupCount={0}
navigateToPage={navigateToPage}
loadProjects={loadProjects}
createProject={() => {}}
updateProjectName={() => {}}
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 '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={() => {}}
/>
);
default:
return (
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh', background: '#f7fafc' }}>
<div style={{ textAlign: 'center' }}>
<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>
);
}
};
if (isLoading) {
return (
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh', background: '#f7fafc' }}>
<div style={{ textAlign: 'center' }}>
<div style={{ fontSize: '16px', color: '#718096' }}>로딩 ...</div>
</div>
</div>
);
}
if (!isAuthenticated) return null;
return (
<ErrorBoundary errorContext={{ user, currentPage, pageParams }}>
<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-EG 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>
);
}
export default App;