Files
TK-BOM-Project/frontend/src/pages/ProjectsPage.jsx
Hyungi Ahn 83b90ef05c
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
feat: 자재 관리 페이지 대규모 개선
- 파이프 수량 계산 로직 수정 (단관 개수가 아닌 실제 길이 기반 계산)
- UI 전면 개편 (DevonThink 스타일의 간결하고 세련된 디자인)
- 자재별 그룹핑 로직 개선:
  * 플랜지: 동일 사양별 그룹핑, WN 스케줄 표시, ORIFICE 풀네임 표시
  * 피팅: 상세 타입 표시 (니플 길이, 엘보 각도/연결, 티 타입, 리듀서 타입 등)
  * 밸브: 동일 사양별 그룹핑, 타입/연결방식/압력 표시
  * 볼트: 크기/재질/길이별 그룹핑 (8SET → 개별 집계)
  * 가스켓: 동일 사양별 그룹핑, 재질/상세내역/두께 분리 표시
  * UNKNOWN: 원본 설명 전체 표시, 동일 항목 그룹핑
- 전체 카테고리 버튼 제거 (표시 복잡도 감소)
- 카테고리별 동적 컬럼 헤더 및 레이아웃 적용
2025-09-09 09:24:45 +09:00

405 lines
17 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useEffect } from 'react';
const ProjectsPage = ({ user }) => {
const [projects, setProjects] = useState([]);
const [loading, setLoading] = useState(true);
const [showCreateForm, setShowCreateForm] = useState(false);
useEffect(() => {
// 실제로는 API에서 프로젝트 데이터를 가져올 예정
// 현재는 더미 데이터 사용
setTimeout(() => {
setProjects([
{
id: 1,
name: '냉동기 시스템 개발',
type: '냉동기',
status: '진행중',
startDate: '2024-01-15',
endDate: '2024-06-30',
deliveryMethod: 'FOB',
progress: 65,
manager: '김철수'
},
{
id: 2,
name: 'BOG 처리 시스템',
type: 'BOG',
status: '계획',
startDate: '2024-02-01',
endDate: '2024-08-15',
deliveryMethod: 'CIF',
progress: 15,
manager: '이영희'
},
{
id: 3,
name: '다이아프람 펌프 제작',
type: '다이아프람',
status: '완료',
startDate: '2023-10-01',
endDate: '2024-01-31',
deliveryMethod: 'FOB',
progress: 100,
manager: '박민수'
}
]);
setLoading(false);
}, 1000);
}, []);
const getStatusColor = (status) => {
const colors = {
'계획': '#ed8936',
'진행중': '#48bb78',
'완료': '#38b2ac',
'보류': '#e53e3e'
};
return colors[status] || '#718096';
};
const getProgressColor = (progress) => {
if (progress >= 80) return '#48bb78';
if (progress >= 50) return '#ed8936';
return '#e53e3e';
};
if (loading) {
return (
<div style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
minHeight: '400px',
fontSize: '16px',
color: '#718096'
}}>
프로젝트 목록을 불러오는 ...
</div>
);
}
return (
<div style={{
padding: '32px',
background: '#f7fafc',
minHeight: '100vh'
}}>
<div style={{ maxWidth: '1200px', margin: '0 auto' }}>
{/* 헤더 */}
<div style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: '32px'
}}>
<div>
<h1 style={{
fontSize: '28px',
fontWeight: '700',
color: '#2d3748',
margin: '0 0 8px 0'
}}>
📋 프로젝트 관리
</h1>
<p style={{
color: '#718096',
fontSize: '16px',
margin: '0'
}}>
전체 프로젝트를 관리하고 진행 상황을 확인하세요.
</p>
</div>
<button
onClick={() => setShowCreateForm(true)}
style={{
padding: '12px 24px',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
color: 'white',
border: 'none',
borderRadius: '8px',
cursor: 'pointer',
fontSize: '14px',
fontWeight: '600',
display: 'flex',
alignItems: 'center',
gap: '8px',
transition: 'transform 0.2s ease'
}}
onMouseEnter={(e) => e.target.style.transform = 'translateY(-1px)'}
onMouseLeave={(e) => e.target.style.transform = 'translateY(0)'}
>
<span></span>
프로젝트
</button>
</div>
{/* 프로젝트 통계 */}
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))',
gap: '16px',
marginBottom: '32px'
}}>
{[
{ label: '전체', count: projects.length, color: '#667eea' },
{ label: '진행중', count: projects.filter(p => p.status === '진행중').length, color: '#48bb78' },
{ label: '완료', count: projects.filter(p => p.status === '완료').length, color: '#38b2ac' },
{ label: '계획', count: projects.filter(p => p.status === '계획').length, color: '#ed8936' }
].map((stat, index) => (
<div key={index} style={{
background: 'white',
padding: '20px',
borderRadius: '12px',
border: '1px solid #e2e8f0',
textAlign: 'center'
}}>
<div style={{
fontSize: '24px',
fontWeight: '700',
color: stat.color,
marginBottom: '4px'
}}>
{stat.count}
</div>
<div style={{
fontSize: '14px',
color: '#718096'
}}>
{stat.label}
</div>
</div>
))}
</div>
{/* 프로젝트 목록 */}
<div style={{
background: 'white',
borderRadius: '12px',
border: '1px solid #e2e8f0',
overflow: 'hidden'
}}>
<div style={{
padding: '20px 24px',
borderBottom: '1px solid #e2e8f0',
background: '#f7fafc'
}}>
<h3 style={{
margin: '0',
fontSize: '16px',
fontWeight: '600',
color: '#2d3748'
}}>
프로젝트 목록 ({projects.length})
</h3>
</div>
<div style={{ overflowX: 'auto' }}>
<table style={{
width: '100%',
borderCollapse: 'collapse'
}}>
<thead>
<tr style={{ background: '#f7fafc' }}>
{['프로젝트명', '유형', '상태', '수주일', '납기일', '납품방법', '진행률', '담당자'].map(header => (
<th key={header} style={{
padding: '12px 16px',
textAlign: 'left',
fontSize: '12px',
fontWeight: '600',
color: '#4a5568',
borderBottom: '1px solid #e2e8f0'
}}>
{header}
</th>
))}
</tr>
</thead>
<tbody>
{projects.map(project => (
<tr key={project.id} style={{
borderBottom: '1px solid #e2e8f0',
transition: 'background 0.2s ease'
}}
onMouseEnter={(e) => e.currentTarget.style.background = '#f7fafc'}
onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}>
<td style={{ padding: '16px', fontWeight: '600', color: '#2d3748' }}>
{project.name}
</td>
<td style={{ padding: '16px' }}>
<span style={{
padding: '4px 8px',
background: '#edf2f7',
borderRadius: '4px',
fontSize: '12px',
fontWeight: '500',
color: '#4a5568'
}}>
{project.type}
</span>
</td>
<td style={{ padding: '16px' }}>
<span style={{
padding: '4px 8px',
background: getStatusColor(project.status) + '20',
color: getStatusColor(project.status),
borderRadius: '4px',
fontSize: '12px',
fontWeight: '600'
}}>
{project.status}
</span>
</td>
<td style={{ padding: '16px', color: '#4a5568' }}>
{project.startDate}
</td>
<td style={{ padding: '16px', color: '#4a5568' }}>
{project.endDate}
</td>
<td style={{ padding: '16px', color: '#4a5568' }}>
{project.deliveryMethod}
</td>
<td style={{ padding: '16px' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<div style={{
flex: 1,
height: '6px',
background: '#e2e8f0',
borderRadius: '3px',
overflow: 'hidden'
}}>
<div style={{
width: `${project.progress}%`,
height: '100%',
background: getProgressColor(project.progress),
transition: 'width 0.3s ease'
}} />
</div>
<span style={{
fontSize: '12px',
fontWeight: '600',
color: getProgressColor(project.progress),
minWidth: '35px'
}}>
{project.progress}%
</span>
</div>
</td>
<td style={{ padding: '16px', color: '#4a5568' }}>
{project.manager}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
{/* 프로젝트가 없을 때 */}
{projects.length === 0 && (
<div style={{
background: 'white',
borderRadius: '12px',
border: '1px solid #e2e8f0',
padding: '60px 40px',
textAlign: 'center'
}}>
<div style={{ fontSize: '48px', marginBottom: '16px' }}>📋</div>
<h3 style={{
fontSize: '18px',
fontWeight: '600',
color: '#2d3748',
margin: '0 0 8px 0'
}}>
등록된 프로젝트가 없습니다
</h3>
<p style={{
color: '#718096',
margin: '0 0 24px 0'
}}>
번째 프로젝트를 등록해보세요.
</p>
<button
onClick={() => setShowCreateForm(true)}
style={{
padding: '12px 24px',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
color: 'white',
border: 'none',
borderRadius: '8px',
cursor: 'pointer',
fontSize: '14px',
fontWeight: '600'
}}
>
프로젝트 등록
</button>
</div>
)}
</div>
{/* 프로젝트 생성 폼 모달 (향후 구현) */}
{showCreateForm && (
<div style={{
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
background: 'rgba(0, 0, 0, 0.5)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: 1000
}}>
<div style={{
background: 'white',
borderRadius: '12px',
padding: '32px',
maxWidth: '500px',
width: '90%',
maxHeight: '90vh',
overflow: 'auto'
}}>
<h3 style={{ margin: '0 0 16px 0' }}> 프로젝트 등록</h3>
<p style={{ color: '#718096', margin: '0 0 24px 0' }}>
기능은 구현될 예정입니다.
</p>
<button
onClick={() => setShowCreateForm(false)}
style={{
padding: '8px 16px',
background: '#e2e8f0',
border: 'none',
borderRadius: '6px',
cursor: 'pointer'
}}
>
닫기
</button>
</div>
</div>
)}
</div>
);
};
export default ProjectsPage;