- tkeg: MUI 기반 대시보드 전면 리디자인 (theme, 메트릭 카드, 프로젝트 Autocomplete, Quick Action, 관리자 섹션) - gateway: tkpurchase 카드 "소모품 관리" → "구매관리"로 복원 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1119 lines
40 KiB
JavaScript
1119 lines
40 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
|
|
|
const DashboardPage = ({
|
|
user,
|
|
projects,
|
|
pendingSignupCount,
|
|
navigateToPage,
|
|
loadProjects,
|
|
createProject,
|
|
updateProjectName,
|
|
deleteProject,
|
|
editingProject,
|
|
setEditingProject,
|
|
editedProjectName,
|
|
setEditedProjectName,
|
|
showCreateProject,
|
|
setShowCreateProject,
|
|
newProjectCode,
|
|
setNewProjectCode,
|
|
newProjectName,
|
|
setNewProjectName,
|
|
newClientName,
|
|
setNewClientName,
|
|
inactiveProjects,
|
|
setInactiveProjects,
|
|
}) => {
|
|
const [selectedProject, setSelectedProject] = useState(null);
|
|
const [showProjectDropdown, setShowProjectDropdown] = useState(false);
|
|
|
|
// 프로젝트 생성 모달 닫기
|
|
const handleCloseCreateProject = () => {
|
|
setShowCreateProject(false);
|
|
setNewProjectCode('');
|
|
setNewProjectName('');
|
|
setNewClientName('');
|
|
};
|
|
|
|
// 프로젝트 선택 처리
|
|
const handleProjectSelect = (project) => {
|
|
setSelectedProject(project);
|
|
setShowProjectDropdown(false);
|
|
};
|
|
|
|
// 프로젝트 비활성화
|
|
const handleDeactivateProject = (project) => {
|
|
const projectId = project.job_no || project.official_project_code || project.id;
|
|
const projectName = project.job_name || project.project_name || projectId;
|
|
|
|
console.log('🔍 비활성화 요청:', { project, projectId, projectName });
|
|
|
|
if (window.confirm(`"${projectName}" 프로젝트를 비활성화하시겠습니까?`)) {
|
|
setInactiveProjects(prev => {
|
|
const newSet = new Set([...prev, projectId]);
|
|
console.log('📦 비활성화 프로젝트 업데이트:', { prev: Array.from(prev), new: Array.from(newSet) });
|
|
return newSet;
|
|
});
|
|
|
|
const selectedProjectId = selectedProject?.job_no || selectedProject?.official_project_code || selectedProject?.id;
|
|
if (selectedProjectId === projectId) {
|
|
setSelectedProject(null);
|
|
}
|
|
setShowProjectDropdown(false);
|
|
}
|
|
};
|
|
|
|
// 프로젝트 활성화
|
|
const handleActivateProject = (project) => {
|
|
setInactiveProjects(prev => {
|
|
const newSet = new Set(prev);
|
|
newSet.delete(project.job_no);
|
|
return newSet;
|
|
});
|
|
};
|
|
|
|
// 프로젝트 삭제 (드롭다운용)
|
|
const handleDeleteProjectFromDropdown = (project, e) => {
|
|
e.stopPropagation();
|
|
if (window.confirm(`"${project.job_name || project.job_no}" 프로젝트를 완전히 삭제하시겠습니까?`)) {
|
|
deleteProject(project.job_no);
|
|
setShowProjectDropdown(false);
|
|
}
|
|
};
|
|
|
|
// 컴포넌트 마운트 시 프로젝트 로드
|
|
useEffect(() => {
|
|
loadProjects();
|
|
}, []);
|
|
|
|
return (
|
|
<div style={{
|
|
padding: '40px',
|
|
background: 'linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%)',
|
|
minHeight: 'calc(100vh - 80px)',
|
|
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'
|
|
}}>
|
|
{/* 대시보드 헤더 */}
|
|
<div style={{ marginBottom: '48px' }}>
|
|
<h1 style={{
|
|
fontSize: '36px',
|
|
fontWeight: '800',
|
|
color: '#1a202c',
|
|
margin: '0 0 12px 0',
|
|
letterSpacing: '-0.025em'
|
|
}}>
|
|
Dashboard
|
|
</h1>
|
|
<p style={{
|
|
color: '#64748b',
|
|
fontSize: '18px',
|
|
margin: 0,
|
|
fontWeight: '400'
|
|
}}>
|
|
TK-MP BOM Management System v2.0 - Project Selection Interface
|
|
</p>
|
|
</div>
|
|
|
|
{/* 프로젝트 선택 섹션 */}
|
|
<div style={{
|
|
background: 'rgba(255, 255, 255, 0.95)',
|
|
backdropFilter: 'blur(10px)',
|
|
borderRadius: '20px',
|
|
padding: '32px',
|
|
boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',
|
|
border: '1px solid rgba(255, 255, 255, 0.2)',
|
|
marginBottom: '40px',
|
|
position: 'relative',
|
|
zIndex: 1000
|
|
}}>
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '28px' }}>
|
|
<div>
|
|
<h2 style={{
|
|
fontSize: '24px',
|
|
fontWeight: '700',
|
|
color: '#0f172a',
|
|
margin: '0 0 4px 0',
|
|
letterSpacing: '-0.025em'
|
|
}}>
|
|
Project Selection
|
|
</h2>
|
|
<p style={{
|
|
fontSize: '14px',
|
|
color: '#64748b',
|
|
margin: 0,
|
|
fontWeight: '400'
|
|
}}>
|
|
Choose a project to access BOM and purchase management
|
|
</p>
|
|
</div>
|
|
<div style={{ display: 'flex', gap: '12px' }}>
|
|
<button
|
|
onClick={() => setShowCreateProject(true)}
|
|
style={{
|
|
background: 'linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%)',
|
|
color: 'white',
|
|
border: 'none',
|
|
borderRadius: '12px',
|
|
padding: '12px 20px',
|
|
cursor: 'pointer',
|
|
fontSize: '14px',
|
|
fontWeight: '600',
|
|
boxShadow: '0 4px 14px 0 rgba(59, 130, 246, 0.39)',
|
|
transition: 'all 0.2s ease',
|
|
letterSpacing: '0.025em'
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.target.style.transform = 'translateY(-2px)';
|
|
e.target.style.boxShadow = '0 8px 25px 0 rgba(59, 130, 246, 0.5)';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.target.style.transform = 'translateY(0)';
|
|
e.target.style.boxShadow = '0 4px 14px 0 rgba(59, 130, 246, 0.39)';
|
|
}}
|
|
>
|
|
New Project
|
|
</button>
|
|
|
|
<button
|
|
onClick={() => {
|
|
if (selectedProject) {
|
|
setEditingProject(selectedProject.job_no);
|
|
setEditedProjectName(selectedProject.job_name || selectedProject.job_no);
|
|
} else {
|
|
alert('먼저 수정할 프로젝트를 선택해주세요.');
|
|
}
|
|
}}
|
|
style={{
|
|
background: 'white',
|
|
color: '#6b7280',
|
|
border: '1px solid #d1d5db',
|
|
borderRadius: '12px',
|
|
padding: '12px 20px',
|
|
cursor: 'pointer',
|
|
fontSize: '14px',
|
|
fontWeight: '600',
|
|
transition: 'all 0.2s ease',
|
|
letterSpacing: '0.025em'
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.target.style.background = '#f9fafb';
|
|
e.target.style.borderColor = '#9ca3af';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.target.style.background = 'white';
|
|
e.target.style.borderColor = '#d1d5db';
|
|
}}
|
|
>
|
|
Rename
|
|
</button>
|
|
|
|
<button
|
|
onClick={() => navigateToPage('inactive-projects')}
|
|
style={{
|
|
background: 'white',
|
|
color: '#6b7280',
|
|
border: '1px solid #d1d5db',
|
|
borderRadius: '12px',
|
|
padding: '12px 20px',
|
|
cursor: 'pointer',
|
|
fontSize: '14px',
|
|
fontWeight: '600',
|
|
transition: 'all 0.2s ease',
|
|
letterSpacing: '0.025em'
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.target.style.background = '#f9fafb';
|
|
e.target.style.borderColor = '#9ca3af';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.target.style.background = 'white';
|
|
e.target.style.borderColor = '#d1d5db';
|
|
}}
|
|
>
|
|
Inactive Management
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 프로젝트 드롭다운 */}
|
|
<div style={{ position: 'relative', marginBottom: '24px', zIndex: 1001 }}>
|
|
<button
|
|
onClick={() => setShowProjectDropdown(!showProjectDropdown)}
|
|
style={{
|
|
width: '100%',
|
|
padding: '16px 20px',
|
|
background: 'white',
|
|
border: '2px solid #e2e8f0',
|
|
borderRadius: '12px',
|
|
fontSize: '16px',
|
|
fontWeight: '500',
|
|
color: selectedProject ? '#1a202c' : '#64748b',
|
|
cursor: 'pointer',
|
|
display: 'flex',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
transition: 'all 0.2s ease',
|
|
boxShadow: '0 2px 8px rgba(0,0,0,0.05)'
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.target.style.borderColor = '#3b82f6';
|
|
e.target.style.boxShadow = '0 4px 12px rgba(59, 130, 246, 0.15)';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.target.style.borderColor = '#e2e8f0';
|
|
e.target.style.boxShadow = '0 2px 8px rgba(0,0,0,0.05)';
|
|
}}
|
|
>
|
|
<div style={{ textAlign: 'left', flex: 1, overflow: 'hidden' }}>
|
|
<div style={{
|
|
fontSize: '16px',
|
|
fontWeight: '600',
|
|
whiteSpace: 'nowrap',
|
|
overflow: 'hidden',
|
|
textOverflow: 'ellipsis'
|
|
}}>
|
|
{selectedProject ? (selectedProject.job_name || selectedProject.job_no) : 'Select a Project'}
|
|
</div>
|
|
{selectedProject && (
|
|
<div style={{
|
|
fontSize: '14px',
|
|
color: '#64748b',
|
|
marginTop: '4px',
|
|
whiteSpace: 'nowrap',
|
|
overflow: 'hidden',
|
|
textOverflow: 'ellipsis'
|
|
}}>
|
|
Code: {selectedProject.job_no} | Client: {selectedProject.client_name || 'N/A'}
|
|
</div>
|
|
)}
|
|
</div>
|
|
<svg
|
|
width="20"
|
|
height="20"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="#64748b"
|
|
strokeWidth="2"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
style={{
|
|
transform: showProjectDropdown ? 'rotate(180deg)' : 'rotate(0deg)',
|
|
transition: 'transform 0.2s ease'
|
|
}}
|
|
>
|
|
<polyline points="6 9 12 15 18 9"></polyline>
|
|
</svg>
|
|
</button>
|
|
|
|
{/* 드롭다운 메뉴 */}
|
|
{showProjectDropdown && (
|
|
<div style={{
|
|
position: 'absolute',
|
|
top: '100%',
|
|
left: 0,
|
|
right: 0,
|
|
marginTop: '8px',
|
|
background: 'white',
|
|
border: '1px solid #e2e8f0',
|
|
borderRadius: '12px',
|
|
boxShadow: '0 20px 40px rgba(0, 0, 0, 0.15)',
|
|
zIndex: 1002,
|
|
maxHeight: '300px',
|
|
overflowY: 'auto',
|
|
width: '100%',
|
|
minWidth: '100%'
|
|
}}>
|
|
{projects.length === 0 ? (
|
|
<div style={{
|
|
padding: '20px',
|
|
textAlign: 'center',
|
|
color: '#64748b',
|
|
fontSize: '14px'
|
|
}}>
|
|
No projects available. Create a new one!
|
|
</div>
|
|
) : (
|
|
projects
|
|
.filter(project => {
|
|
const projectId = project.job_no || project.official_project_code || project.id;
|
|
return !inactiveProjects.has(projectId);
|
|
})
|
|
.map((project) => (
|
|
<div
|
|
key={project.job_no}
|
|
style={{
|
|
borderBottom: '1px solid #f1f5f9',
|
|
transition: 'background 0.15s ease',
|
|
wordWrap: 'break-word',
|
|
overflow: 'hidden',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between'
|
|
}}
|
|
>
|
|
<div
|
|
onClick={() => handleProjectSelect(project)}
|
|
style={{
|
|
padding: '16px 20px',
|
|
cursor: 'pointer',
|
|
flex: 1
|
|
}}
|
|
onMouseEnter={(e) => e.target.closest('div').style.background = '#f8fafc'}
|
|
onMouseLeave={(e) => e.target.closest('div').style.background = 'white'}
|
|
>
|
|
<div style={{
|
|
fontSize: '16px',
|
|
fontWeight: '600',
|
|
color: '#1a202c',
|
|
marginBottom: '4px',
|
|
whiteSpace: 'nowrap',
|
|
overflow: 'hidden',
|
|
textOverflow: 'ellipsis'
|
|
}}>
|
|
{project.job_name || project.job_no}
|
|
</div>
|
|
<div style={{
|
|
fontSize: '14px',
|
|
color: '#64748b',
|
|
whiteSpace: 'nowrap',
|
|
overflow: 'hidden',
|
|
textOverflow: 'ellipsis'
|
|
}}>
|
|
Code: {project.job_no} | Client: {project.client_name || 'N/A'}
|
|
</div>
|
|
</div>
|
|
|
|
{/* 프로젝트 관리 버튼들 */}
|
|
<div style={{
|
|
display: 'flex',
|
|
gap: '8px',
|
|
padding: '16px 20px 16px 0',
|
|
alignItems: 'center'
|
|
}}>
|
|
<button
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
handleDeactivateProject(project);
|
|
}}
|
|
style={{
|
|
background: '#fef3c7',
|
|
color: '#92400e',
|
|
border: '1px solid #fbbf24',
|
|
borderRadius: '6px',
|
|
padding: '6px 12px',
|
|
cursor: 'pointer',
|
|
fontSize: '12px',
|
|
fontWeight: '500',
|
|
transition: 'all 0.2s ease'
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.target.style.background = '#fde68a';
|
|
e.target.style.borderColor = '#f59e0b';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.target.style.background = '#fef3c7';
|
|
e.target.style.borderColor = '#fbbf24';
|
|
}}
|
|
>
|
|
비활성
|
|
</button>
|
|
|
|
<button
|
|
onClick={(e) => handleDeleteProjectFromDropdown(project, e)}
|
|
style={{
|
|
background: '#fee2e2',
|
|
color: '#dc2626',
|
|
border: '1px solid #f87171',
|
|
borderRadius: '6px',
|
|
padding: '6px 12px',
|
|
cursor: 'pointer',
|
|
fontSize: '12px',
|
|
fontWeight: '500',
|
|
transition: 'all 0.2s ease'
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.target.style.background = '#fecaca';
|
|
e.target.style.borderColor = '#ef4444';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.target.style.background = '#fee2e2';
|
|
e.target.style.borderColor = '#f87171';
|
|
}}
|
|
>
|
|
삭제
|
|
</button>
|
|
</div>
|
|
</div>
|
|
))
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* 프로젝트가 선택된 경우 - 프로젝트 관련 메뉴 */}
|
|
{selectedProject && (
|
|
<div style={{
|
|
background: 'rgba(255, 255, 255, 0.9)',
|
|
backdropFilter: 'blur(10px)',
|
|
borderRadius: '20px',
|
|
padding: '32px',
|
|
boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',
|
|
border: '1px solid rgba(255, 255, 255, 0.2)',
|
|
marginBottom: '40px',
|
|
position: 'relative',
|
|
zIndex: 1
|
|
}}>
|
|
<h2 style={{
|
|
fontSize: '24px',
|
|
fontWeight: '700',
|
|
color: '#0f172a',
|
|
margin: '0 0 28px 0',
|
|
letterSpacing: '-0.025em'
|
|
}}>
|
|
Project Management
|
|
</h2>
|
|
|
|
<div style={{
|
|
display: 'grid',
|
|
gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
|
|
gap: '24px'
|
|
}}>
|
|
{/* 통합 BOM 관리 */}
|
|
<div
|
|
onClick={() => navigateToPage('unified-bom', { selectedProject })}
|
|
style={{
|
|
background: 'white',
|
|
borderRadius: '16px',
|
|
padding: '32px',
|
|
boxShadow: '0 8px 20px rgba(0, 0, 0, 0.08)',
|
|
border: '1px solid #e2e8f0',
|
|
cursor: 'pointer',
|
|
transition: 'all 0.3s ease',
|
|
textAlign: 'center'
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.currentTarget.style.transform = 'translateY(-5px)';
|
|
e.currentTarget.style.boxShadow = '0 12px 30px rgba(0, 0, 0, 0.15)';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.currentTarget.style.transform = 'translateY(0)';
|
|
e.currentTarget.style.boxShadow = '0 8px 20px rgba(0, 0, 0, 0.08)';
|
|
}}
|
|
>
|
|
<div style={{
|
|
width: '64px',
|
|
height: '64px',
|
|
borderRadius: '16px',
|
|
background: 'linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%)',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
margin: '0 auto 20px auto',
|
|
color: 'white',
|
|
fontSize: '24px',
|
|
fontWeight: '700'
|
|
}}>
|
|
BOM
|
|
</div>
|
|
<h3 style={{
|
|
fontSize: '20px',
|
|
fontWeight: '700',
|
|
color: '#1a202c',
|
|
margin: '0 0 12px 0'
|
|
}}>
|
|
BOM Management
|
|
</h3>
|
|
<p style={{
|
|
color: '#64748b',
|
|
fontSize: '14px',
|
|
margin: 0,
|
|
lineHeight: '1.5'
|
|
}}>
|
|
Upload, manage revisions, and classify materials in one unified workspace
|
|
</p>
|
|
|
|
{/* 기능 미리보기 */}
|
|
<div style={{
|
|
display: 'flex',
|
|
justifyContent: 'center',
|
|
gap: '12px',
|
|
marginTop: '16px'
|
|
}}>
|
|
<div style={{
|
|
background: 'rgba(59, 130, 246, 0.1)',
|
|
color: '#3b82f6',
|
|
padding: '4px 8px',
|
|
borderRadius: '6px',
|
|
fontSize: '12px',
|
|
fontWeight: '500'
|
|
}}>
|
|
📤 Upload
|
|
</div>
|
|
<div style={{
|
|
background: 'rgba(245, 158, 11, 0.1)',
|
|
color: '#f59e0b',
|
|
padding: '4px 8px',
|
|
borderRadius: '6px',
|
|
fontSize: '12px',
|
|
fontWeight: '500'
|
|
}}>
|
|
📊 Revisions
|
|
</div>
|
|
<div style={{
|
|
background: 'rgba(16, 185, 129, 0.1)',
|
|
color: '#10b981',
|
|
padding: '4px 8px',
|
|
borderRadius: '6px',
|
|
fontSize: '12px',
|
|
fontWeight: '500'
|
|
}}>
|
|
📋 Materials
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 구매신청 관리 */}
|
|
<div
|
|
onClick={() => navigateToPage('purchase-request', { selectedProject })}
|
|
style={{
|
|
background: 'white',
|
|
borderRadius: '16px',
|
|
padding: '32px',
|
|
boxShadow: '0 8px 20px rgba(0, 0, 0, 0.08)',
|
|
border: '1px solid #e2e8f0',
|
|
cursor: 'pointer',
|
|
transition: 'all 0.3s ease',
|
|
textAlign: 'center'
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.currentTarget.style.transform = 'translateY(-5px)';
|
|
e.currentTarget.style.boxShadow = '0 12px 30px rgba(0, 0, 0, 0.15)';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.currentTarget.style.transform = 'translateY(0)';
|
|
e.currentTarget.style.boxShadow = '0 8px 20px rgba(0, 0, 0, 0.08)';
|
|
}}
|
|
>
|
|
<div style={{
|
|
width: '64px',
|
|
height: '64px',
|
|
borderRadius: '16px',
|
|
background: 'linear-gradient(135deg, #10b981 0%, #059669 100%)',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
margin: '0 auto 20px auto',
|
|
color: 'white',
|
|
fontSize: '16px',
|
|
fontWeight: '700'
|
|
}}>
|
|
REQ
|
|
</div>
|
|
<h3 style={{
|
|
fontSize: '20px',
|
|
fontWeight: '700',
|
|
color: '#1a202c',
|
|
margin: '0 0 12px 0'
|
|
}}>
|
|
Purchase Request Management
|
|
</h3>
|
|
<p style={{
|
|
color: '#64748b',
|
|
fontSize: '14px',
|
|
margin: 0,
|
|
lineHeight: '1.5'
|
|
}}>
|
|
Manage purchase requests and export materials to Excel for procurement.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* 관리자 메뉴 (Admin 이상만 표시) */}
|
|
{user?.role === 'admin' && (
|
|
<div style={{
|
|
background: 'rgba(255, 255, 255, 0.9)',
|
|
backdropFilter: 'blur(10px)',
|
|
borderRadius: '20px',
|
|
padding: '32px',
|
|
boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',
|
|
border: '1px solid rgba(255, 255, 255, 0.2)',
|
|
marginBottom: '40px',
|
|
position: 'relative',
|
|
zIndex: 1
|
|
}}>
|
|
<h2 style={{
|
|
fontSize: '24px',
|
|
fontWeight: '700',
|
|
color: '#0f172a',
|
|
margin: '0 0 28px 0',
|
|
letterSpacing: '-0.025em'
|
|
}}>
|
|
System Administration
|
|
</h2>
|
|
|
|
<div style={{
|
|
display: 'grid',
|
|
gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))',
|
|
gap: '24px'
|
|
}}>
|
|
{/* 사용자 관리 */}
|
|
<div
|
|
onClick={() => navigateToPage('user-management')}
|
|
style={{
|
|
background: 'white',
|
|
borderRadius: '16px',
|
|
padding: '24px',
|
|
boxShadow: '0 8px 20px rgba(0, 0, 0, 0.08)',
|
|
border: '1px solid #e2e8f0',
|
|
cursor: 'pointer',
|
|
transition: 'all 0.3s ease',
|
|
textAlign: 'center'
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.currentTarget.style.transform = 'translateY(-3px)';
|
|
e.currentTarget.style.boxShadow = '0 12px 25px rgba(0, 0, 0, 0.12)';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.currentTarget.style.transform = 'translateY(0)';
|
|
e.currentTarget.style.boxShadow = '0 8px 20px rgba(0, 0, 0, 0.08)';
|
|
}}
|
|
>
|
|
<div style={{
|
|
width: '48px',
|
|
height: '48px',
|
|
borderRadius: '12px',
|
|
background: 'linear-gradient(135deg, #f59e0b 0%, #d97706 100%)',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
margin: '0 auto 16px auto',
|
|
color: 'white',
|
|
fontSize: '14px',
|
|
fontWeight: '700'
|
|
}}>
|
|
USER
|
|
</div>
|
|
<h3 style={{
|
|
fontSize: '16px',
|
|
fontWeight: '700',
|
|
color: '#1a202c',
|
|
margin: '0 0 8px 0'
|
|
}}>
|
|
User Management
|
|
</h3>
|
|
<p style={{
|
|
color: '#64748b',
|
|
fontSize: '12px',
|
|
margin: 0,
|
|
lineHeight: '1.4'
|
|
}}>
|
|
Manage user accounts and permissions
|
|
</p>
|
|
{pendingSignupCount > 0 && (
|
|
<div style={{
|
|
marginTop: '8px',
|
|
padding: '4px 8px',
|
|
background: '#ef4444',
|
|
color: 'white',
|
|
borderRadius: '6px',
|
|
fontSize: '12px',
|
|
fontWeight: '600'
|
|
}}>
|
|
{pendingSignupCount} pending
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* 시스템 설정 */}
|
|
<div
|
|
onClick={() => navigateToPage('system-settings')}
|
|
style={{
|
|
background: 'white',
|
|
borderRadius: '16px',
|
|
padding: '24px',
|
|
boxShadow: '0 8px 20px rgba(0, 0, 0, 0.08)',
|
|
border: '1px solid #e2e8f0',
|
|
cursor: 'pointer',
|
|
transition: 'all 0.3s ease',
|
|
textAlign: 'center'
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.currentTarget.style.transform = 'translateY(-3px)';
|
|
e.currentTarget.style.boxShadow = '0 12px 25px rgba(0, 0, 0, 0.12)';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.currentTarget.style.transform = 'translateY(0)';
|
|
e.currentTarget.style.boxShadow = '0 8px 20px rgba(0, 0, 0, 0.08)';
|
|
}}
|
|
>
|
|
<div style={{
|
|
width: '48px',
|
|
height: '48px',
|
|
borderRadius: '12px',
|
|
background: 'linear-gradient(135deg, #6366f1 0%, #4f46e5 100%)',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
margin: '0 auto 16px auto',
|
|
color: 'white',
|
|
fontSize: '14px',
|
|
fontWeight: '700'
|
|
}}>
|
|
SYS
|
|
</div>
|
|
<h3 style={{
|
|
fontSize: '16px',
|
|
fontWeight: '700',
|
|
color: '#1a202c',
|
|
margin: '0 0 8px 0'
|
|
}}>
|
|
System Settings
|
|
</h3>
|
|
<p style={{
|
|
color: '#64748b',
|
|
fontSize: '12px',
|
|
margin: 0,
|
|
lineHeight: '1.4'
|
|
}}>
|
|
Configure system preferences and settings
|
|
</p>
|
|
</div>
|
|
|
|
{/* 시스템 로그 */}
|
|
<div
|
|
onClick={() => navigateToPage('system-logs')}
|
|
style={{
|
|
background: 'white',
|
|
borderRadius: '16px',
|
|
padding: '24px',
|
|
boxShadow: '0 8px 20px rgba(0, 0, 0, 0.08)',
|
|
border: '1px solid #e2e8f0',
|
|
cursor: 'pointer',
|
|
transition: 'all 0.3s ease',
|
|
textAlign: 'center'
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.currentTarget.style.transform = 'translateY(-3px)';
|
|
e.currentTarget.style.boxShadow = '0 12px 25px rgba(0, 0, 0, 0.12)';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.currentTarget.style.transform = 'translateY(0)';
|
|
e.currentTarget.style.boxShadow = '0 8px 20px rgba(0, 0, 0, 0.08)';
|
|
}}
|
|
>
|
|
<div style={{
|
|
width: '48px',
|
|
height: '48px',
|
|
borderRadius: '12px',
|
|
background: 'linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%)',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
margin: '0 auto 16px auto',
|
|
color: 'white',
|
|
fontSize: '14px',
|
|
fontWeight: '700'
|
|
}}>
|
|
LOG
|
|
</div>
|
|
<h3 style={{
|
|
fontSize: '16px',
|
|
fontWeight: '700',
|
|
color: '#1a202c',
|
|
margin: '0 0 8px 0'
|
|
}}>
|
|
System Logs
|
|
</h3>
|
|
<p style={{
|
|
color: '#64748b',
|
|
fontSize: '12px',
|
|
margin: 0,
|
|
lineHeight: '1.4'
|
|
}}>
|
|
View system activity and error logs
|
|
</p>
|
|
</div>
|
|
|
|
{/* 로그 모니터링 */}
|
|
<div
|
|
onClick={() => navigateToPage('log-monitoring')}
|
|
style={{
|
|
background: 'white',
|
|
borderRadius: '16px',
|
|
padding: '24px',
|
|
boxShadow: '0 8px 20px rgba(0, 0, 0, 0.08)',
|
|
border: '1px solid #e2e8f0',
|
|
cursor: 'pointer',
|
|
transition: 'all 0.3s ease',
|
|
textAlign: 'center'
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.currentTarget.style.transform = 'translateY(-3px)';
|
|
e.currentTarget.style.boxShadow = '0 12px 25px rgba(0, 0, 0, 0.12)';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.currentTarget.style.transform = 'translateY(0)';
|
|
e.currentTarget.style.boxShadow = '0 8px 20px rgba(0, 0, 0, 0.08)';
|
|
}}
|
|
>
|
|
<div style={{
|
|
width: '48px',
|
|
height: '48px',
|
|
borderRadius: '12px',
|
|
background: 'linear-gradient(135deg, #06b6d4 0%, #0891b2 100%)',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
margin: '0 auto 16px auto',
|
|
color: 'white',
|
|
fontSize: '14px',
|
|
fontWeight: '700'
|
|
}}>
|
|
MON
|
|
</div>
|
|
<h3 style={{
|
|
fontSize: '16px',
|
|
fontWeight: '700',
|
|
color: '#1a202c',
|
|
margin: '0 0 8px 0'
|
|
}}>
|
|
Log Monitoring
|
|
</h3>
|
|
<p style={{
|
|
color: '#64748b',
|
|
fontSize: '12px',
|
|
margin: 0,
|
|
lineHeight: '1.4'
|
|
}}>
|
|
Real-time system monitoring and alerts
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* 시스템 현황 섹션 */}
|
|
<div style={{
|
|
background: 'rgba(255, 255, 255, 0.9)',
|
|
backdropFilter: 'blur(10px)',
|
|
borderRadius: '20px',
|
|
padding: '32px',
|
|
boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',
|
|
border: '1px solid rgba(255, 255, 255, 0.2)',
|
|
position: 'relative',
|
|
zIndex: 1
|
|
}}>
|
|
<h2 style={{
|
|
fontSize: '24px',
|
|
fontWeight: '700',
|
|
color: '#0f172a',
|
|
margin: '0 0 28px 0',
|
|
letterSpacing: '-0.025em'
|
|
}}>
|
|
System Overview
|
|
</h2>
|
|
<div style={{
|
|
display: 'grid',
|
|
gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))',
|
|
gap: '20px'
|
|
}}>
|
|
{/* 등록된 프로젝트 */}
|
|
<div style={{
|
|
background: '#f0f4f8',
|
|
borderRadius: '12px',
|
|
padding: '20px',
|
|
textAlign: 'center',
|
|
boxShadow: '0 4px 10px rgba(0,0,0,0.05)'
|
|
}}>
|
|
<div style={{ fontSize: '32px', fontWeight: '700', color: '#3b82f6', marginBottom: '8px' }}>
|
|
{projects.length || 0}
|
|
</div>
|
|
<div style={{ fontSize: '14px', color: '#475569', fontWeight: '500' }}>Registered Projects</div>
|
|
</div>
|
|
{/* 선택된 프로젝트 */}
|
|
<div style={{
|
|
background: '#f0f4f8',
|
|
borderRadius: '12px',
|
|
padding: '20px',
|
|
textAlign: 'center',
|
|
boxShadow: '0 4px 10px rgba(0,0,0,0.05)'
|
|
}}>
|
|
<div style={{ fontSize: '32px', fontWeight: '700', color: selectedProject ? '#22c55e' : '#64748b', marginBottom: '8px' }}>
|
|
{selectedProject ? '1' : '0'}
|
|
</div>
|
|
<div style={{ fontSize: '14px', color: '#475569', fontWeight: '500' }}>Selected Project</div>
|
|
</div>
|
|
{/* 현재 권한 */}
|
|
<div style={{
|
|
background: '#f0f4f8',
|
|
borderRadius: '12px',
|
|
padding: '20px',
|
|
textAlign: 'center',
|
|
boxShadow: '0 4px 10px rgba(0,0,0,0.05)'
|
|
}}>
|
|
<div style={{ fontSize: '32px', fontWeight: '700', color: '#f97316', marginBottom: '8px' }}>
|
|
{user?.role === 'admin' ? 'Admin' : 'User'}
|
|
</div>
|
|
<div style={{ fontSize: '14px', color: '#475569', fontWeight: '500' }}>Current Role</div>
|
|
</div>
|
|
{/* 시스템 상태 */}
|
|
<div style={{
|
|
background: '#f0f4f8',
|
|
borderRadius: '12px',
|
|
padding: '20px',
|
|
textAlign: 'center',
|
|
boxShadow: '0 4px 10px rgba(0,0,0,0.05)'
|
|
}}>
|
|
<div style={{ fontSize: '32px', fontWeight: '700', color: '#22c55e', marginBottom: '8px' }}>
|
|
Active
|
|
</div>
|
|
<div style={{ fontSize: '14px', color: '#475569', fontWeight: '500' }}>System Status</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 프로젝트 생성 모달 */}
|
|
{showCreateProject && (
|
|
<div style={{
|
|
position: 'fixed',
|
|
top: 0,
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
background: 'rgba(0, 0, 0, 0.6)',
|
|
display: 'flex',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
zIndex: 1000
|
|
}}>
|
|
<div style={{
|
|
background: 'white',
|
|
borderRadius: '16px',
|
|
padding: '32px',
|
|
boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.25)',
|
|
width: '90%',
|
|
maxWidth: '500px',
|
|
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'
|
|
}}>
|
|
<h3 style={{
|
|
fontSize: '24px',
|
|
fontWeight: '700',
|
|
color: '#1a202c',
|
|
margin: '0 0 24px 0',
|
|
letterSpacing: '-0.025em'
|
|
}}>
|
|
Create New Project
|
|
</h3>
|
|
<div style={{ marginBottom: '20px' }}>
|
|
<label style={{ display: 'block', fontSize: '14px', color: '#475569', marginBottom: '8px', fontWeight: '500' }}>
|
|
Project Code
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={newProjectCode}
|
|
onChange={(e) => setNewProjectCode(e.target.value)}
|
|
style={{
|
|
width: '100%',
|
|
padding: '12px',
|
|
borderRadius: '8px',
|
|
border: '1px solid #cbd5e1',
|
|
fontSize: '16px',
|
|
boxSizing: 'border-box'
|
|
}}
|
|
placeholder="e.g., J24-001"
|
|
/>
|
|
</div>
|
|
<div style={{ marginBottom: '20px' }}>
|
|
<label style={{ display: 'block', fontSize: '14px', color: '#475569', marginBottom: '8px', fontWeight: '500' }}>
|
|
Project Name
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={newProjectName}
|
|
onChange={(e) => setNewProjectName(e.target.value)}
|
|
style={{
|
|
width: '100%',
|
|
padding: '12px',
|
|
borderRadius: '8px',
|
|
border: '1px solid #cbd5e1',
|
|
fontSize: '16px',
|
|
boxSizing: 'border-box'
|
|
}}
|
|
placeholder="e.g., Ulsan SK Energy Expansion"
|
|
/>
|
|
</div>
|
|
<div style={{ marginBottom: '24px' }}>
|
|
<label style={{ display: 'block', fontSize: '14px', color: '#475569', marginBottom: '8px', fontWeight: '500' }}>
|
|
Client Name (Optional)
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={newClientName}
|
|
onChange={(e) => setNewClientName(e.target.value)}
|
|
style={{
|
|
width: '100%',
|
|
padding: '12px',
|
|
borderRadius: '8px',
|
|
border: '1px solid #cbd5e1',
|
|
fontSize: '16px',
|
|
boxSizing: 'border-box'
|
|
}}
|
|
placeholder="e.g., Samsung Engineering"
|
|
/>
|
|
</div>
|
|
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: '12px' }}>
|
|
<button
|
|
onClick={handleCloseCreateProject}
|
|
style={{
|
|
background: '#e2e8f0',
|
|
color: '#475569',
|
|
border: 'none',
|
|
borderRadius: '10px',
|
|
padding: '12px 24px',
|
|
cursor: 'pointer',
|
|
fontSize: '16px',
|
|
fontWeight: '600',
|
|
transition: 'background 0.2s ease'
|
|
}}
|
|
onMouseEnter={(e) => e.target.style.background = '#cbd5e1'}
|
|
onMouseLeave={(e) => e.target.style.background = '#e2e8f0'}
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
onClick={createProject}
|
|
style={{
|
|
background: 'linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%)',
|
|
color: 'white',
|
|
border: 'none',
|
|
borderRadius: '10px',
|
|
padding: '12px 24px',
|
|
cursor: 'pointer',
|
|
fontSize: '16px',
|
|
fontWeight: '600',
|
|
boxShadow: '0 4px 14px 0 rgba(59, 130, 246, 0.39)',
|
|
transition: 'all 0.2s ease'
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.target.style.transform = 'translateY(-2px)';
|
|
e.target.style.boxShadow = '0 8px 25px 0 rgba(59, 130, 246, 0.5)';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.target.style.transform = 'translateY(0)';
|
|
e.target.style.boxShadow = '0 4px 14px 0 rgba(59, 130, 246, 0.39)';
|
|
}}
|
|
>
|
|
Create Project
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default DashboardPage; |