Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
- 🎨 UI/UX 개선: 데본씽크 스타일 모던 디자인 적용 - 📁 컴포넌트 구조 개선: 폴더별 체계적 관리 (common/, bom/, materials/) - 🔧 BOM 관리 페이지 리팩토링: NewMaterialsPage → BOMManagementPage + 카테고리별 컴포넌트 분리 - 💾 구매신청 기능 개선: 선택된 자재 비활성화, 제목 편집, 엑셀 다운로드 - 📊 자재 표시 개선: 타입/서브타입 컬럼 정리, 상세 정보 복원 - 🐛 CSS 빌드 오류 수정: NewMaterialsPage.css 문법 오류 해결 - 📚 문서화: PAGES_GUIDE.md 추가, README에 Docker 캐시 문제 해결 가이드 추가 - 🔄 API 개선: 구매신청 자재 조회, 제목 수정 엔드포인트 추가
399 lines
14 KiB
JavaScript
399 lines
14 KiB
JavaScript
import React, { useState } from 'react';
|
|
|
|
const InactiveProjectsPage = ({
|
|
onNavigate,
|
|
user,
|
|
projects,
|
|
inactiveProjects,
|
|
onActivateProject,
|
|
onDeleteProject
|
|
}) => {
|
|
const [selectedProjects, setSelectedProjects] = useState(new Set());
|
|
|
|
// 비활성 프로젝트 목록 필터링
|
|
const inactiveProjectList = projects.filter(project =>
|
|
inactiveProjects.has(project.job_no)
|
|
);
|
|
|
|
// 프로젝트 선택/해제
|
|
const handleProjectSelect = (projectNo) => {
|
|
setSelectedProjects(prev => {
|
|
const newSet = new Set(prev);
|
|
if (newSet.has(projectNo)) {
|
|
newSet.delete(projectNo);
|
|
} else {
|
|
newSet.add(projectNo);
|
|
}
|
|
return newSet;
|
|
});
|
|
};
|
|
|
|
// 전체 선택/해제
|
|
const handleSelectAll = () => {
|
|
if (selectedProjects.size === inactiveProjectList.length) {
|
|
setSelectedProjects(new Set());
|
|
} else {
|
|
setSelectedProjects(new Set(inactiveProjectList.map(p => p.job_no)));
|
|
}
|
|
};
|
|
|
|
// 선택된 프로젝트들 활성화
|
|
const handleBulkActivate = () => {
|
|
if (selectedProjects.size === 0) {
|
|
alert('활성화할 프로젝트를 선택해주세요.');
|
|
return;
|
|
}
|
|
|
|
if (window.confirm(`선택된 ${selectedProjects.size}개 프로젝트를 활성화하시겠습니까?`)) {
|
|
selectedProjects.forEach(projectNo => {
|
|
const project = projects.find(p => p.job_no === projectNo);
|
|
if (project) {
|
|
onActivateProject(project);
|
|
}
|
|
});
|
|
setSelectedProjects(new Set());
|
|
}
|
|
};
|
|
|
|
// 선택된 프로젝트들 삭제
|
|
const handleBulkDelete = () => {
|
|
if (selectedProjects.size === 0) {
|
|
alert('삭제할 프로젝트를 선택해주세요.');
|
|
return;
|
|
}
|
|
|
|
if (window.confirm(`선택된 ${selectedProjects.size}개 프로젝트를 완전히 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.`)) {
|
|
selectedProjects.forEach(projectNo => {
|
|
onDeleteProject(projectNo);
|
|
});
|
|
setSelectedProjects(new Set());
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div style={{
|
|
padding: '40px',
|
|
background: 'linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%)',
|
|
minHeight: '100vh'
|
|
}}>
|
|
{/* 헤더 */}
|
|
<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'
|
|
}}>
|
|
<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'
|
|
}}>
|
|
Inactive Projects Management
|
|
</h2>
|
|
<p style={{
|
|
fontSize: '14px',
|
|
color: '#64748b',
|
|
margin: 0,
|
|
fontWeight: '400'
|
|
}}>
|
|
Manage deactivated projects - activate or permanently delete
|
|
</p>
|
|
</div>
|
|
<button
|
|
onClick={() => onNavigate('dashboard')}
|
|
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';
|
|
}}
|
|
>
|
|
Back to Dashboard
|
|
</button>
|
|
</div>
|
|
|
|
{/* 통계 정보 */}
|
|
<div style={{
|
|
display: 'grid',
|
|
gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))',
|
|
gap: '20px',
|
|
marginBottom: '32px'
|
|
}}>
|
|
<div style={{
|
|
background: 'linear-gradient(135deg, #fef3c7 0%, #fde68a 100%)',
|
|
padding: '20px',
|
|
borderRadius: '12px',
|
|
textAlign: 'center'
|
|
}}>
|
|
<div style={{ fontSize: '28px', fontWeight: '700', color: '#92400e', marginBottom: '4px' }}>
|
|
{inactiveProjectList.length}
|
|
</div>
|
|
<div style={{ fontSize: '14px', color: '#92400e', fontWeight: '500' }}>
|
|
Inactive Projects
|
|
</div>
|
|
</div>
|
|
|
|
<div style={{
|
|
background: 'linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%)',
|
|
padding: '20px',
|
|
borderRadius: '12px',
|
|
textAlign: 'center'
|
|
}}>
|
|
<div style={{ fontSize: '28px', fontWeight: '700', color: '#1d4ed8', marginBottom: '4px' }}>
|
|
{selectedProjects.size}
|
|
</div>
|
|
<div style={{ fontSize: '14px', color: '#1d4ed8', fontWeight: '500' }}>
|
|
Selected
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 일괄 작업 버튼들 */}
|
|
{inactiveProjectList.length > 0 && (
|
|
<div style={{ display: 'flex', gap: '12px', marginBottom: '24px' }}>
|
|
<button
|
|
onClick={handleSelectAll}
|
|
style={{
|
|
background: 'white',
|
|
color: '#6b7280',
|
|
border: '1px solid #d1d5db',
|
|
borderRadius: '8px',
|
|
padding: '10px 16px',
|
|
cursor: 'pointer',
|
|
fontSize: '14px',
|
|
fontWeight: '500',
|
|
transition: 'all 0.2s ease'
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.target.style.background = '#f9fafb';
|
|
e.target.style.borderColor = '#9ca3af';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.target.style.background = 'white';
|
|
e.target.style.borderColor = '#d1d5db';
|
|
}}
|
|
>
|
|
{selectedProjects.size === inactiveProjectList.length ? 'Deselect All' : 'Select All'}
|
|
</button>
|
|
|
|
<button
|
|
onClick={handleBulkActivate}
|
|
disabled={selectedProjects.size === 0}
|
|
style={{
|
|
background: selectedProjects.size > 0 ? 'linear-gradient(135deg, #10b981 0%, #059669 100%)' : '#e5e7eb',
|
|
color: selectedProjects.size > 0 ? 'white' : '#9ca3af',
|
|
border: 'none',
|
|
borderRadius: '8px',
|
|
padding: '10px 16px',
|
|
cursor: selectedProjects.size > 0 ? 'pointer' : 'not-allowed',
|
|
fontSize: '14px',
|
|
fontWeight: '500',
|
|
transition: 'all 0.2s ease'
|
|
}}
|
|
>
|
|
Activate Selected ({selectedProjects.size})
|
|
</button>
|
|
|
|
<button
|
|
onClick={handleBulkDelete}
|
|
disabled={selectedProjects.size === 0}
|
|
style={{
|
|
background: selectedProjects.size > 0 ? 'linear-gradient(135deg, #ef4444 0%, #dc2626 100%)' : '#e5e7eb',
|
|
color: selectedProjects.size > 0 ? 'white' : '#9ca3af',
|
|
border: 'none',
|
|
borderRadius: '8px',
|
|
padding: '10px 16px',
|
|
cursor: selectedProjects.size > 0 ? 'pointer' : 'not-allowed',
|
|
fontSize: '14px',
|
|
fontWeight: '500',
|
|
transition: 'all 0.2s ease'
|
|
}}
|
|
>
|
|
Delete Selected ({selectedProjects.size})
|
|
</button>
|
|
</div>
|
|
)}
|
|
</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)'
|
|
}}>
|
|
<h3 style={{
|
|
fontSize: '20px',
|
|
fontWeight: '700',
|
|
color: '#0f172a',
|
|
margin: '0 0 24px 0',
|
|
letterSpacing: '-0.025em'
|
|
}}>
|
|
Inactive Projects List
|
|
</h3>
|
|
|
|
{inactiveProjectList.length === 0 ? (
|
|
<div style={{
|
|
textAlign: 'center',
|
|
padding: '60px 20px',
|
|
color: '#64748b'
|
|
}}>
|
|
<div style={{ fontSize: '48px', marginBottom: '16px' }}>📂</div>
|
|
<div style={{ fontSize: '18px', fontWeight: '600', marginBottom: '8px' }}>
|
|
No Inactive Projects
|
|
</div>
|
|
<div style={{ fontSize: '14px' }}>
|
|
All projects are currently active
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div style={{
|
|
display: 'grid',
|
|
gap: '16px'
|
|
}}>
|
|
{inactiveProjectList.map((project) => (
|
|
<div
|
|
key={project.job_no}
|
|
style={{
|
|
background: 'white',
|
|
border: '1px solid #e2e8f0',
|
|
borderRadius: '12px',
|
|
padding: '20px',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
transition: 'all 0.2s ease',
|
|
boxShadow: '0 2px 8px rgba(0,0,0,0.05)'
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.target.style.borderColor = '#cbd5e1';
|
|
e.target.style.boxShadow = '0 4px 12px rgba(0,0,0,0.1)';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.target.style.borderColor = '#e2e8f0';
|
|
e.target.style.boxShadow = '0 2px 8px rgba(0,0,0,0.05)';
|
|
}}
|
|
>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: '16px', flex: 1 }}>
|
|
<input
|
|
type="checkbox"
|
|
checked={selectedProjects.has(project.job_no)}
|
|
onChange={() => handleProjectSelect(project.job_no)}
|
|
style={{
|
|
width: '18px',
|
|
height: '18px',
|
|
cursor: 'pointer'
|
|
}}
|
|
/>
|
|
|
|
<div style={{ flex: 1 }}>
|
|
<div style={{
|
|
fontSize: '18px',
|
|
fontWeight: '600',
|
|
color: '#1a202c',
|
|
marginBottom: '4px'
|
|
}}>
|
|
{project.job_name || project.job_no}
|
|
</div>
|
|
<div style={{
|
|
fontSize: '14px',
|
|
color: '#64748b'
|
|
}}>
|
|
Code: {project.job_no} | Client: {project.client_name || 'N/A'}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div style={{ display: 'flex', gap: '12px' }}>
|
|
<button
|
|
onClick={() => {
|
|
if (window.confirm(`"${project.job_name || project.job_no}" 프로젝트를 활성화하시겠습니까?`)) {
|
|
onActivateProject(project);
|
|
}
|
|
}}
|
|
style={{
|
|
background: 'linear-gradient(135deg, #10b981 0%, #059669 100%)',
|
|
color: 'white',
|
|
border: 'none',
|
|
borderRadius: '8px',
|
|
padding: '8px 16px',
|
|
cursor: 'pointer',
|
|
fontSize: '14px',
|
|
fontWeight: '500',
|
|
transition: 'all 0.2s ease'
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.target.style.transform = 'translateY(-1px)';
|
|
e.target.style.boxShadow = '0 4px 12px rgba(16, 185, 129, 0.4)';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.target.style.transform = 'translateY(0)';
|
|
e.target.style.boxShadow = 'none';
|
|
}}
|
|
>
|
|
Activate
|
|
</button>
|
|
|
|
<button
|
|
onClick={() => {
|
|
if (window.confirm(`"${project.job_name || project.job_no}" 프로젝트를 완전히 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.`)) {
|
|
onDeleteProject(project.job_no);
|
|
}
|
|
}}
|
|
style={{
|
|
background: 'linear-gradient(135deg, #ef4444 0%, #dc2626 100%)',
|
|
color: 'white',
|
|
border: 'none',
|
|
borderRadius: '8px',
|
|
padding: '8px 16px',
|
|
cursor: 'pointer',
|
|
fontSize: '14px',
|
|
fontWeight: '500',
|
|
transition: 'all 0.2s ease'
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.target.style.transform = 'translateY(-1px)';
|
|
e.target.style.boxShadow = '0 4px 12px rgba(239, 68, 68, 0.4)';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.target.style.transform = 'translateY(0)';
|
|
e.target.style.boxShadow = 'none';
|
|
}}
|
|
>
|
|
Delete
|
|
</button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default InactiveProjectsPage;
|