feat: 공동작업을 위한 프로젝트 구조 최적화 및 가이드 배포
This commit is contained in:
168
backup_temp/src/components/AssetManagementView.tsx
Normal file
168
backup_temp/src/components/AssetManagementView.tsx
Normal file
@@ -0,0 +1,168 @@
|
||||
import { useState } from 'react'
|
||||
import { idcServers, idcStorages, IdcServer } from '../data/idcData'
|
||||
import ServerDetailModal from './ServerDetailModal'
|
||||
|
||||
const AssetManagementView = () => {
|
||||
const [viewMode, setViewMode] = useState<'server' | 'storage'>('server')
|
||||
const [selectedServer, setSelectedServer] = useState<IdcServer | null>(null)
|
||||
|
||||
return (
|
||||
<div className="asset-management" style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
|
||||
<div className="content-header">
|
||||
<div className="content-title">전산자산관리대장 (IDC)</div>
|
||||
<div style={{ display: 'flex', gap: '12px' }}>
|
||||
<button
|
||||
className={`btn ${viewMode === 'server' ? 'btn-primary' : 'btn-outline'}`}
|
||||
onClick={() => setViewMode('server')}
|
||||
>
|
||||
서버 목록
|
||||
</button>
|
||||
<button
|
||||
className={`btn ${viewMode === 'storage' ? 'btn-primary' : 'btn-outline'}`}
|
||||
onClick={() => setViewMode('storage')}
|
||||
>
|
||||
스토리지 목록
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="table-container">
|
||||
<div style={{ padding: '24px 0 16px 0' }}>
|
||||
<h3 style={{ fontSize: '1.125rem', fontWeight: 600, color: 'var(--primary-color)', margin: 0 }}>
|
||||
{viewMode === 'server' ? 'IDC 서버 상세 정보' : 'IDC 스토리지 상세 정보'}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
{viewMode === 'server' ? (
|
||||
<table className="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>회사</th>
|
||||
<th>서버번호</th>
|
||||
<th>구분</th>
|
||||
<th>설치위치</th>
|
||||
<th>관리자</th>
|
||||
<th>IP 주소</th>
|
||||
<th>접속 정보</th>
|
||||
<th>H/W 사양</th>
|
||||
<th>OS</th>
|
||||
<th>구매일</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{idcServers.map((server) => (
|
||||
<tr key={server.serverNo} onClick={() => setSelectedServer(server)} style={{ cursor: 'pointer' }}>
|
||||
<td style={{ fontWeight: 600 }}>{server.company}</td>
|
||||
<td style={{ color: 'var(--primary-color)', fontWeight: 500 }}>{server.serverNo}</td>
|
||||
<td>
|
||||
<div>{server.category}</div>
|
||||
{server.remarks && <div style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>{server.remarks}</div>}
|
||||
</td>
|
||||
<td>{server.location}</td>
|
||||
<td>
|
||||
<div style={{ fontWeight: 500 }}>{server.managerPrimary ? `정: ${server.managerPrimary}` : '정: -'}</div>
|
||||
<div style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>{server.managerSecondary ? `부: ${server.managerSecondary}` : '부: -'}</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>{server.ip1}</div>
|
||||
{server.ip2 && <div style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>{server.ip2}</div>}
|
||||
</td>
|
||||
<td>
|
||||
{server.remoteAccess.map((access, idx) => (
|
||||
<div key={idx} style={{
|
||||
marginBottom: idx < server.remoteAccess.length - 1 ? '8px' : 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '2px'
|
||||
}}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
|
||||
<span style={{ fontSize: '0.7rem', backgroundColor: '#f3f4f6', padding: '1px 4px', borderRadius: '2px', color: 'var(--text-muted)', fontWeight: 600 }}>{access.tool}</span>
|
||||
<span style={{ fontSize: '0.8125rem', fontWeight: 500 }}>{access.id}</span>
|
||||
</div>
|
||||
<div style={{ fontSize: '0.75rem', color: 'var(--text-muted)', paddingLeft: '4px', borderLeft: '1px solid var(--border-color)', marginLeft: '4px' }}>
|
||||
PW: <span style={{ color: 'var(--text-main)' }}>{access.pw}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</td>
|
||||
<td style={{ fontSize: '0.8125rem' }}>
|
||||
<div style={{ fontWeight: 500 }}>{server.model}</div>
|
||||
<div style={{ color: 'var(--text-muted)' }}>{server.cpu} / {server.ram}</div>
|
||||
<div style={{ color: 'var(--text-muted)' }}>{server.storage.join(' + ')}</div>
|
||||
</td>
|
||||
<td style={{ fontSize: '0.8125rem' }}>{server.os}</td>
|
||||
<td style={{ fontSize: '0.8125rem' }}>{server.purchaseDate}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
) : (
|
||||
<table className="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>회사</th>
|
||||
<th>서버번호</th>
|
||||
<th>구분</th>
|
||||
<th>설치위치</th>
|
||||
<th>관리자</th>
|
||||
<th>IP 주소</th>
|
||||
<th>접속 정보</th>
|
||||
<th>모델명</th>
|
||||
<th>용량</th>
|
||||
<th>구매일</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{idcStorages.map((storage) => (
|
||||
<tr key={storage.serverNo}>
|
||||
<td style={{ fontWeight: 600 }}>{storage.company}</td>
|
||||
<td style={{ color: 'var(--primary-color)', fontWeight: 500 }}>{storage.serverNo}</td>
|
||||
<td>
|
||||
<div>{storage.category}</div>
|
||||
{storage.remarks && <div style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>{storage.remarks}</div>}
|
||||
</td>
|
||||
<td>{storage.location}</td>
|
||||
<td>
|
||||
<span style={{ fontWeight: 500 }}>정: {storage.managerPrimary}</span>
|
||||
<span style={{ fontSize: '0.75rem', color: 'var(--text-muted)', marginLeft: '8px' }}>부: {storage.managerSecondary}</span>
|
||||
</td>
|
||||
<td>{storage.ip}</td>
|
||||
<td>
|
||||
{storage.remoteAccess.map((access, idx) => (
|
||||
<div key={idx} style={{
|
||||
marginBottom: idx < storage.remoteAccess.length - 1 ? '8px' : 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '2px'
|
||||
}}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
|
||||
<span style={{ fontSize: '0.7rem', backgroundColor: '#f3f4f6', padding: '1px 4px', borderRadius: '2px', color: 'var(--text-muted)', fontWeight: 600 }}>{access.tool}</span>
|
||||
<span style={{ fontSize: '0.8125rem', fontWeight: 500 }}>{access.id}</span>
|
||||
</div>
|
||||
<div style={{ fontSize: '0.75rem', color: 'var(--text-muted)', paddingLeft: '4px', borderLeft: '1px solid var(--border-color)', marginLeft: '4px' }}>
|
||||
PW: <span style={{ color: 'var(--text-main)' }}>{access.pw}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</td>
|
||||
<td>{storage.model}</td>
|
||||
<td style={{ fontWeight: 600 }}>{storage.capacity}</td>
|
||||
<td>{storage.purchaseDate}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{selectedServer && (
|
||||
<ServerDetailModal
|
||||
server={selectedServer}
|
||||
onClose={() => setSelectedServer(null)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AssetManagementView
|
||||
51
backup_temp/src/components/DashboardView.tsx
Normal file
51
backup_temp/src/components/DashboardView.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import { mockCategories } from '../data/mockData'
|
||||
|
||||
const DashboardView = () => {
|
||||
return (
|
||||
<div>
|
||||
<div className="content-header">
|
||||
<div className="content-title">대시보드</div>
|
||||
</div>
|
||||
|
||||
<div className="dashboard-stats">
|
||||
<div className="stat-card">
|
||||
<div className="stat-label">전체 자산</div>
|
||||
<div className="stat-value">8개</div>
|
||||
</div>
|
||||
{mockCategories.map(cat => (
|
||||
<div key={cat.id} className="stat-card">
|
||||
<div className="stat-label">{cat.name}</div>
|
||||
<div className="stat-value">{cat.count}개</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="card">
|
||||
<h3>최근 변경 내역</h3>
|
||||
<table className="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>날짜</th>
|
||||
<th>내용</th>
|
||||
<th>사용자</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>2023-04-11</td>
|
||||
<td>PC 신규 등록</td>
|
||||
<td>이관형</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2023-04-10</td>
|
||||
<td>모니터 부서 할당 변경</td>
|
||||
<td>관리자</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DashboardView
|
||||
87
backup_temp/src/components/HardwareManagementView.tsx
Normal file
87
backup_temp/src/components/HardwareManagementView.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import { useState } from 'react'
|
||||
import { mockHardwareSpecs, HardwareSpec } from '../data/mockData'
|
||||
|
||||
const SpecModal = ({ spec, onClose }: { spec: HardwareSpec, onClose: () => void }) => {
|
||||
return (
|
||||
<div style={{
|
||||
position: 'fixed', top: 0, left: 0, width: '100%', height: '100%',
|
||||
backgroundColor: 'rgba(0,0,0,0.5)', display: 'flex', justifyContent: 'center', alignItems: 'center',
|
||||
zIndex: 1000
|
||||
}}>
|
||||
<div className="card" style={{ width: '600px', maxWidth: '90%' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '20px' }}>
|
||||
<h2>상세 사양 정보</h2>
|
||||
<button className="btn" onClick={onClose}>×</button>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 2fr', gap: '10px' }}>
|
||||
<strong>PC명:</strong> <span>{spec.pcName}</span>
|
||||
<strong>사용자:</strong> <span>{spec.userName}</span>
|
||||
<strong>부서:</strong> <span>{spec.department}</span>
|
||||
<strong>OS:</strong> <span>{spec.os}</span>
|
||||
<strong>CPU:</strong> <span>{spec.cpu}</span>
|
||||
<strong>Memory:</strong> <span>{spec.memory}</span>
|
||||
<strong>Disk:</strong> <span>{spec.disk}</span>
|
||||
<strong>MAC:</strong> <span>{spec.macAddress}</span>
|
||||
<strong>IP:</strong> <span>{spec.ipAddress}</span>
|
||||
<strong>Graphic:</strong> <span>{spec.graphicCard}</span>
|
||||
</div>
|
||||
<div style={{ marginTop: '20px', textAlign: 'right' }}>
|
||||
<button className="btn btn-primary" onClick={onClose}>닫기</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const HardwareManagementView = () => {
|
||||
const [selectedSpec, setSelectedSpec] = useState<HardwareSpec | null>(null)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="content-header">
|
||||
<div className="content-title">H/W 사양 정보</div>
|
||||
</div>
|
||||
|
||||
<table className="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>PC명</th>
|
||||
<th>부서</th>
|
||||
<th>사용자</th>
|
||||
<th>OS</th>
|
||||
<th>CPU</th>
|
||||
<th>IP주소</th>
|
||||
<th>상세</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{mockHardwareSpecs.map(spec => (
|
||||
<tr key={spec.id}>
|
||||
<td>{spec.pcName}</td>
|
||||
<td>{spec.department}</td>
|
||||
<td>{spec.userName}</td>
|
||||
<td>{spec.os.split(' ')[2]}</td>
|
||||
<td title={spec.cpu}>{spec.cpu.split('@')[0]}</td>
|
||||
<td>{spec.ipAddress}</td>
|
||||
<td>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
style={{ padding: '4px 8px', fontSize: '0.8rem' }}
|
||||
onClick={() => setSelectedSpec(spec)}
|
||||
>
|
||||
보기
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{selectedSpec && (
|
||||
<SpecModal spec={selectedSpec} onClose={() => setSelectedSpec(null)} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default HardwareManagementView
|
||||
144
backup_temp/src/components/ServerDetailModal.tsx
Normal file
144
backup_temp/src/components/ServerDetailModal.tsx
Normal file
@@ -0,0 +1,144 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { IdcServer } from '../data/idcData';
|
||||
|
||||
interface ServerDetailModalProps {
|
||||
server: IdcServer;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const ServerDetailModal: React.FC<ServerDetailModalProps> = ({ server, onClose }) => {
|
||||
// ESC 키로 모달 닫기
|
||||
useEffect(() => {
|
||||
const handleEsc = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
window.addEventListener('keydown', handleEsc);
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleEsc);
|
||||
};
|
||||
}, [onClose]);
|
||||
|
||||
return (
|
||||
<div className="modal-overlay" onClick={onClose}>
|
||||
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="modal-header">
|
||||
<h2 style={{ margin: 0, fontSize: '1.125rem', fontWeight: 600 }}>{server.category} ({server.serverNo})</h2>
|
||||
<button className="modal-close-btn" onClick={onClose} aria-label="Close modal">×</button>
|
||||
</div>
|
||||
|
||||
<div className="modal-body">
|
||||
<div className="detail-grid">
|
||||
{/* Row 1 */}
|
||||
<div className="detail-item">
|
||||
<label>회사 구분</label>
|
||||
<div className="detail-value">{server.company}</div>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<label>서버 번호</label>
|
||||
<div className="detail-value" style={{ color: 'var(--primary-color)', fontWeight: 600 }}>{server.serverNo}</div>
|
||||
</div>
|
||||
|
||||
{/* Row 2 */}
|
||||
<div className="detail-item">
|
||||
<label>자산명(용도)</label>
|
||||
<div className="detail-value">{server.category}</div>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<label>설치 위치</label>
|
||||
<div className="detail-value">{server.location}</div>
|
||||
</div>
|
||||
|
||||
{/* Row 3: 관리자 추가 */}
|
||||
<div className="detail-item full-width">
|
||||
<label>관리 담당자</label>
|
||||
<div className="detail-value">
|
||||
<span style={{ fontWeight: 600, color: 'var(--text-main)' }}>정: {server.managerPrimary}</span>
|
||||
<span style={{ marginLeft: '16px', color: 'var(--text-muted)' }}>부: {server.managerSecondary}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Row 4 */}
|
||||
<div className="detail-item">
|
||||
<label>IP 주소 1</label>
|
||||
<div className="detail-value">{server.ip1 || '-'}</div>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<label>IP 주소 2</label>
|
||||
<div className="detail-value">{server.ip2 || '-'}</div>
|
||||
</div>
|
||||
|
||||
{/* Row 5 */}
|
||||
<div className="detail-item full-width">
|
||||
<label>원격 접속 정보</label>
|
||||
<div className="detail-value">
|
||||
{server.remoteAccess.length > 0 ? (
|
||||
<div style={{ display: 'flex', gap: '16px', flexWrap: 'wrap' }}>
|
||||
{server.remoteAccess.map((access, idx) => (
|
||||
<div key={idx} style={{ display: 'flex', alignItems: 'center', gap: '8px', padding: '4px 8px', backgroundColor: 'var(--bg-muted)', borderRadius: '4px', border: '1px solid var(--border-color)' }}>
|
||||
<span style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-muted)' }}>{access.tool}</span>
|
||||
<span style={{ fontWeight: 500 }}>{access.id}</span>
|
||||
<span style={{ color: 'var(--border-color)' }}>|</span>
|
||||
<span>PW: <span style={{ color: 'var(--text-main)' }}>{access.pw}</span></span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : '-'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Row 6 */}
|
||||
<div className="detail-item">
|
||||
<label>제조사 및 모델명</label>
|
||||
<div className="detail-value">{server.model || '-'}</div>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<label>OS</label>
|
||||
<div className="detail-value">{server.os || '-'}</div>
|
||||
</div>
|
||||
|
||||
{/* Row 7 */}
|
||||
<div className="detail-item">
|
||||
<label>CPU</label>
|
||||
<div className="detail-value">{server.cpu || '-'}</div>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<label>RAM</label>
|
||||
<div className="detail-value">{server.ram || '-'}</div>
|
||||
</div>
|
||||
|
||||
{/* Row 8 */}
|
||||
<div className="detail-item full-width">
|
||||
<label>Storage (디스크 구성)</label>
|
||||
<div className="detail-value">{server.storage.length > 0 ? server.storage.join(' + ') : '-'}</div>
|
||||
</div>
|
||||
|
||||
{/* Row 9 */}
|
||||
<div className="detail-item">
|
||||
<label>구매일자</label>
|
||||
<div className="detail-value">{server.purchaseDate || '-'}</div>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<label>모니터링 여부</label>
|
||||
<div className="detail-value">{server.monitoring || '-'}</div>
|
||||
</div>
|
||||
|
||||
{/* Row 10 */}
|
||||
<div className="detail-item full-width">
|
||||
<label>비고 및 특이사항</label>
|
||||
<div className="detail-value" style={{ minHeight: '40px' }}>{server.remarks || '-'}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="modal-footer">
|
||||
<button className="btn btn-outline" onClick={onClose} style={{ marginRight: '8px' }}>닫기</button>
|
||||
<button className="btn btn-primary" onClick={onClose}>수정(저장)</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ServerDetailModal;
|
||||
Reference in New Issue
Block a user