Merge origin/setting into SW_Table and resolve conflicts

This commit is contained in:
2026-04-23 18:22:34 +09:00
58 changed files with 7495 additions and 2508 deletions

View File

@@ -27,7 +27,7 @@ export interface HardwareAsset {
용량?: string;
담당자_정?: string;
담당자_부?: string;
구매?: string;
구매연월?: string;
금액?: string;
납품업체: string;
품의서명: string;
@@ -40,8 +40,10 @@ export interface HardwareAsset {
비고?: string;
현사용조직?: string;
이전사용조직?: string;
detail_purpose?: string;
상세용도?: string;
메인보드?: string;
보관위치?: string;
현재상태?: string;
}
export interface SoftwareAsset {
@@ -51,7 +53,7 @@ export interface SoftwareAsset {
법인: string;
부서?: string;
제품명: string;
구매: string;
구매연월: string;
구독일?: string;
만료일?: string;
라이선스유형?: string;
@@ -62,13 +64,11 @@ export interface SoftwareAsset {
계정명: string;
납품업체: string;
비고: string;
자산번호?: string;
플랫폼명?: string;
결제수단?: string;
결제일?: string;
연결카드번호?: string;
당월청구액?: string;
시작일?: string;
}
export interface SWUser {
@@ -100,24 +100,21 @@ export interface MasterAssetData {
mobile: HardwareAsset[];
subSw: SoftwareAsset[];
permSw: SoftwareAsset[];
cloud: SoftwareAsset[];
swUsers: SWUser[];
swUsers: any[]; // { sw_id, userData: [] } 형태로 처리
logs: HardwareLog[];
sw: SoftwareAsset[];
hw: HardwareAsset[];
}
const HW_TABS = ['개인PC', '서버', '스토리지', '전산비품', '모바일기기'];
const SW_TABS = ['구독SW', '영구SW', '클라우드'];
const PC_HEADERS = ['법인', '자산코드', '사용자', '위치', '모델명', '메인보드', 'CPU', 'GPU', 'RAM', 'SSD1', 'SSD2', 'HDD1', 'HDD2', 'IP주소', 'HW사양', '구매', '금액', '납품업체', '품의서명', '비고'];
const SERVER_HEADERS = ['구매법인', '자산번호', '구매일자', '유형', '용도', '상세내용', '현사용조직', '이전사용조직', '설치위치', '담당자(정)', '담당자(부)', 'IP 주소 1', 'IP 주소 2', '원격도구', '서버 ID', '서버 PW', '모델명', 'OS', 'CPU', 'RAM', 'GPU', 'Storage 1', 'Storage 2', 'Storage 3', '모니터링', '비고'];
const STORAGE_HEADERS = ['구매법인', '유형', '자산코드', '명칭', '위치', '모델명', '용량', '담당자(정)', '담당자(부)', 'IP주소', 'MAC주소', '구매', '금액', '납품업체', '품의서명', '비고'];
const EQUIP_HEADERS = ['구매법인', '비품유형', '자산코드', '명칭', '위치', '관리자', 'IP주소', 'MACaddress', 'HW사양', 'OS', '구매', '금액', '납품업체', '품의서명', '비고'];
const MOBILE_HEADERS = ['구매법인', '자산코드', '명칭', '위치', '관리자', '기기유형', 'OS', '구매', '금액', '납품업체', '품의서명', '비고'];
const PC_HEADERS = ['법인', '자산코드', '사용자', '위치', '모델명', '메인보드', 'CPU', 'GPU', 'RAM', 'SSD1', 'SSD2', 'HDD1', 'HDD2', 'IP주소', 'HW사양', '구매연월', '금액', '납품업체', '품의서명', '비고'];
const SERVER_HEADERS = ['구매법인', '자산번호', '구매연월', '유형', '용도', '상세내용', '현사용조직', '이전사용조직', '설치위치', '담당자(정)', '담당자(부)', 'IP 주소 1', 'IP 주소 2', '원격도구', '서버 ID', '서버 PW', '모델명', 'OS', 'CPU', 'RAM', 'GPU', 'Storage 1', 'Storage 2', 'Storage 3', '모니터링', '비고'];
const STORAGE_HEADERS = ['구매법인', '유형', '자산코드', '명칭', '위치', '모델명', '용량', '담당자(정)', '담당자(부)', 'IP주소', 'MAC주소', '구매연월', '금액', '납품업체', '품의서명', '비고'];
const EQUIP_HEADERS = ['구매법인', '비품유형', '자산코드', '명칭', '위치', '관리자', 'IP주소', 'MACaddress', 'HW사양', 'OS', '구매연월', '금액', '납품업체', '품의서명', '비고'];
const MOBILE_HEADERS = ['구매법인', '자산코드', '명칭', '위치', '관리자', '기기유형', 'OS', '구매연월', '금액', '납품업체', '품의서명', '비고'];
const SUB_SW_HEADERS = ['ID', '분야', '법인', '부서', '제품명', '구매', '만료일', '라이선스유형', '금액', '수량', '계정명', '납품업체', '비고'];
const PERM_SW_HEADERS = ['ID', '분야', '법인', '부서', '제품명', '구매', '라이선스키', '금액', '수량', '계정명', '납품업체', '비고'];
const SUB_SW_HEADERS = ['ID', '분야', '법인', '부서', '제품명', '구매연월', '만료일', '라이선스유형', '금액', '수량', '계정명', '납품업체', '비고'];
const PERM_SW_HEADERS = ['ID', '분야', '법인', '부서', '제품명', '구매연월', '라이선스키', '금액', '수량', '계정명', '납품업체', '비고'];
const CLOUD_HEADERS = ['ID', '플랫폼명', '법인', '부서', '사용용도(제품명)', '계정명', '결제수단', '결제일', '연결카드번호', '당월청구액', '비고'];
export function downloadTemplate() {
@@ -149,13 +146,13 @@ export function downloadTemplate() {
export function exportToExcel(masterData: MasterAssetData) {
const wb = XLSX.utils.book_new();
const exportMap = [
{ tab: '개인PC', list: masterData.pc, headers: PC_HEADERS, map: (a: any) => [a., a., a., a., a., a., a.CPU, a.GPU, a.RAM, a.SSD1, a.SSD2, a.HDD1, a.HDD2, a.IP주소, a.HW사양, a., a., a., a., a.] },
{ tab: '서버', list: masterData.server, headers: SERVER_HEADERS, map: (a: any) => [a., a., a., a.storage유형 || '물리', a., a., a., a., a., a._정, a._부, a.IP주소, a.IP2, a., a.ID, a.PW, a., a.OS, a.CPU, a.RAM, a.GPU, a.SSD1, a.SSD2, a.HDD1, a., a.] },
{ tab: '스토리지', list: masterData.storage, headers: STORAGE_HEADERS, map: (a: any) => [a., a.storage유형, a., a., a., a., a., a._정, a._부, a.IP주소, a.MACaddress, a., a., a., a., a.] },
{ tab: '전산비품', list: masterData.equip, headers: EQUIP_HEADERS, map: (a: any) => [a., a., a., a., a., a., a.IP주소, a.MACaddress, a.HW사양, a.OS, a., a., a., a., a.] },
{ tab: '모바일기기', list: masterData.mobile, headers: MOBILE_HEADERS, map: (a: any) => [a., a., a., a., a., a.type, a.OS, a., a., a., a., a.] },
{ tab: '구독SW', list: masterData.subSw, headers: SUB_SW_HEADERS, map: (a: any) => [a.id, a., a., a., a., a., a., a., a., a., a., a., a.] },
{ tab: '영구SW', list: masterData.permSw, headers: PERM_SW_HEADERS, map: (a: any) => [a.id, a., a., a., a., a., a., a., a., a., a., a.] }
{ tab: '개인PC', list: masterData.pc, headers: PC_HEADERS, map: (a: any) => [a., a., a., a., a., a., a.CPU, a.GPU, a.RAM, a.SSD1, a.SSD2, a.HDD1, a.HDD2, a.IP주소, a.HW사양, a.||a., a., a., a., a.] },
{ tab: '서버', list: masterData.server, headers: SERVER_HEADERS, map: (a: any) => [a., a., a.||a., a.storage유형 || '물리', a., a., a., a., a., a._정, a._부, a.IP주소, a.IP2, a., a.ID, a.PW, a., a.OS, a.CPU, a.RAM, a.GPU, a.SSD1, a.SSD2, a.HDD1, a., a.] },
{ tab: '스토리지', list: masterData.storage, headers: STORAGE_HEADERS, map: (a: any) => [a., a.storage유형, a., a., a., a., a., a._정, a._부, a.IP주소, a.MACaddress, a.||a., a., a., a., a.] },
{ tab: '전산비품', list: masterData.equip, headers: EQUIP_HEADERS, map: (a: any) => [a., a., a., a., a., a., a.IP주소, a.MACaddress, a.HW사양, a.OS, a.||a., a., a., a., a.] },
{ tab: '모바일기기', list: masterData.mobile, headers: MOBILE_HEADERS, map: (a: any) => [a., a., a., a., a., a.type, a.OS, a.||a., a., a., a., a.] },
{ tab: '구독SW', list: masterData.subSw, headers: SUB_SW_HEADERS, map: (a: any) => [a.id, a., a., a., a., a.||a., a., a., a., a., a., a., a.] },
{ tab: '영구SW', list: masterData.permSw, headers: PERM_SW_HEADERS, map: (a: any) => [a.id, a., a., a., a., a.||a., a., a., a., a., a., a.] }
];
exportMap.forEach(m => {
@@ -171,23 +168,23 @@ export async function parseExcel(file: File): Promise<MasterAssetData> {
reader.onload = (e) => {
try {
const workbook = XLSX.read(e.target?.result, { type: 'binary' });
const data: MasterAssetData = { pc: [], server: [], storage: [], equip: [], mobile: [], subSw: [], permSw: [], cloud: [], swUsers: [], logs: [], sw: [], hw: [] };
const data: MasterAssetData = { pc: [], server: [], storage: [], equip: [], mobile: [], subSw: [], permSw: [], swUsers: [], logs: [] };
workbook.SheetNames.forEach(sheetName => {
const rows = XLSX.utils.sheet_to_json(workbook.Sheets[sheetName]) as any[];
if (sheetName === '개인PC') {
rows.forEach(r => data.pc.push({ id: Math.random().toString(36).substring(2, 9), type: '개인PC', 법인: r['법인']||'', 자산코드: r['자산코드']||'', 사용자: r['사용자']||'', 위치: r['위치']||'', 모델명: r['모델명']||'', 메인보드: r['메인보드']||'', CPU: r['CPU']||'', GPU: r['GPU']||'', RAM: r['RAM']||'', SSD1: r['SSD1']||'', SSD2: r['SSD2']||'', HDD1: r['HDD1']||'', HDD2: r['HDD2']||'', IP주소: r['IP주소']||'', HW사양: r['HW사양']||'', 구매일: r['구매일']||'', 금액: r['금액']||'', 납품업체: r['납품업체']||'', 품의서명: r['품의서명']||'', 비고: r['비고']||'', : '', MACaddress: '', OS: '', : '' }));
rows.forEach(r => data.pc.push({ id: Math.random().toString(36).substring(2, 9), type: '개인PC', 법인: r['법인']||'', 자산코드: r['자산코드']||'', 사용자: r['사용자']||'', 위치: r['위치']||'', 모델명: r['모델명']||'', 메인보드: r['메인보드']||'', CPU: r['CPU']||'', GPU: r['GPU']||'', RAM: r['RAM']||'', SSD1: r['SSD1']||'', SSD2: r['SSD2']||'', HDD1: r['HDD1']||'', HDD2: r['HDD2']||'', IP주소: r['IP주소']||'', HW사양: r['HW사양']||'', 구매일: r['구매일']||r['구매연월']||'', 금액: r['금액']||'', 납품업체: r['납품업체']||'', 품의서명: r['품의서명']||'', 비고: r['비고']||'', : '', MACaddress: '', OS: '', : '' }));
} else if (sheetName === '서버') {
rows.forEach(r => data.server.push({ id: Math.random().toString(36).substring(2, 9), type: '서버', 법인: r['구매법인']||r['법인']||'', 자산코드: r['자산번호']||r['자산코드']||'', 구매: r['구매일자']||r['구매일']||'', storage유형: r['유형']||'물리', 용도: r['용도']||'', 상세: r['상세내용']||'', 현사용조직: r['현사용조직']||'', 이전사용조직: r['이전사용조직']||'', 위치: r['설치위치']||r['위치']||'', 담당자_정: r['담당자(정)']||'', 담당자_부: r['담당자(부)']||'', IP주소: r['IP 주소 1']||r['IP주소']||'', IP2: r['IP 주소 2']||'', 원격접속: r['원격도구']||r['원격접속']||'', 서버ID: r['서버 ID']||r['서버ID']||'', 서버PW: r['서버 PW']||r['서버PW']||'', 모델명: r['모델명']||'', OS: r['OS']||'', CPU: r['CPU']||'', RAM: r['RAM']||'', GPU: r['GPU']||'', SSD1: r['Storage 1']||r['SSD1']||'', SSD2: r['Storage 2']||r['SSD2']||'', HDD1: r['Storage 3']||r['HDD1']||'', 모니터링: r['모니터링']||'', 비고: r['비고']||'', : '', : '', MACaddress: '', HW사양: '', : '', : '', : '' }));
rows.forEach(r => data.server.push({ id: Math.random().toString(36).substring(2, 9), type: '서버', 법인: r['구매법인']||r['법인']||'', 자산코드: r['자산번호']||r['자산코드']||'', 구매연월: r['구매연월']||r['구매일자']||r['구매일']||'', storage유형: r['유형']||'물리', 용도: r['용도']||'', 상세: r['상세내용']||'', 현사용조직: r['현사용조직']||'', 이전사용조직: r['이전사용조직']||'', 위치: r['설치위치']||r['위치']||'', 담당자_정: r['담당자(정)']||'', 담당자_부: r['담당자(부)']||'', IP주소: r['IP 주소 1']||r['IP주소']||'', IP2: r['IP 주소 2']||'', 원격접속: r['원격도구']||r['원격접속']||'', 서버ID: r['서버 ID']||r['서버ID']||'', 서버PW: r['서버 PW']||r['서버PW']||'', 모델명: r['모델명']||'', OS: r['OS']||'', CPU: r['CPU']||'', RAM: r['RAM']||'', GPU: r['GPU']||'', SSD1: r['Storage 1']||r['SSD1']||'', SSD2: r['Storage 2']||r['SSD2']||'', HDD1: r['Storage 3']||r['HDD1']||'', 모니터링: r['모니터링']||'', 비고: r['비고']||'', : '', : '', MACaddress: '', HW사양: '', : '', : '', : '' }));
} else if (sheetName === '스토리지') {
rows.forEach(r => data.storage.push({ id: Math.random().toString(36).substring(2, 9), type: '스토리지', 법인: r['구매법인']||r['법인']||'', storage유형: r['유형']||'', 자산코드: r['자산코드']||'', 명칭: r['명칭']||'', 위치: r['위치']||'', 모델명: r['모델명']||'', 용량: r['용량']||'', 담당자_정: r['담당자(정)']||'', 담당자_부: r['담당자(부)']||'', IP주소: r['IP주소']||'', MACaddress: r['MAC주소']||'', 구매: r['구매일']||'', 금액: r['금액']||'', 납품업체: r['납품업체']||'', 품의서명: r['품의서명']||'', 비고: r['비고']||'', HW사양: '', OS: '', : '' }));
rows.forEach(r => data.storage.push({ id: Math.random().toString(36).substring(2, 9), type: '스토리지', 법인: r['구매법인']||r['법인']||'', storage유형: r['유형']||'', 자산코드: r['자산코드']||'', 명칭: r['명칭']||'', 위치: r['위치']||'', 모델명: r['모델명']||'', 용량: r['용량']||'', 담당자_정: r['담당자(정)']||'', 담당자_부: r['담당자(부)']||'', IP주소: r['IP주소']||'', MACaddress: r['MAC주소']||'', 구매연월: r['구매연월']||r['구매일']||'', 금액: r['금액']||'', 납품업체: r['납품업체']||'', 품의서명: r['품의서명']||'', 비고: r['비고']||'', HW사양: '', OS: '', : '' }));
} else if (sheetName === '전산비품') {
rows.forEach(r => data.equip.push({ id: Math.random().toString(36).substring(2, 9), type: '전산비품', 법인: r['구매법인']||r['법인']||'', 비품유형: r['비품유형']||r['유형']||'', 자산코드: r['자산코드']||'', 명칭: r['명칭']||'', 위치: r['위치']||'', 관리자: r['관리자']||'', IP주소: r['IP주소']||'', MACaddress: r['MACaddress']||'', HW사양: r['HW사양']||'', OS: r['OS']||'', 구매: r['구매일']||'', 금액: r['금액']||'', 납품업체: r['납품업체']||'', 품의서명: r['품의서명']||'', 비고: r['비고']||'' }));
rows.forEach(r => data.equip.push({ id: Math.random().toString(36).substring(2, 9), type: '전산비품', 법인: r['구매법인']||r['법인']||'', 비품유형: r['비품유형']||r['유형']||'', 자산코드: r['자산코드']||'', 명칭: r['명칭']||'', 위치: r['위치']||'', 관리자: r['관리자']||'', IP주소: r['IP주소']||'', MACaddress: r['MACaddress']||'', HW사양: r['HW사양']||'', OS: r['OS']||'', 구매연월: r['구매연월']||r['구매일']||'', 금액: r['금액']||'', 납품업체: r['납품업체']||'', 품의서명: r['품의서명']||'', 비고: r['비고']||'' }));
} else if (sheetName === '모바일기기') {
rows.forEach(r => data.mobile.push({ id: Math.random().toString(36).substring(2, 9), type: '모바일기기', 법인: r['구매법인']||r['법인']||'', 자산코드: r['자산코드']||'', 명칭: r['명칭']||'', 위치: r['위치']||'', 관리자: r['관리자']||'', OS: r['OS']||'', 구매: r['구매일']||'', 금액: r['금액']||'', 납품업체: r['납품업체']||'', 품의서명: r['품의서명']||'', 비고: r['비고']||'', IP주소: '', MACaddress: '', HW사양: '' }));
rows.forEach(r => data.mobile.push({ id: Math.random().toString(36).substring(2, 9), type: '모바일기기', 법인: r['구매법인']||r['법인']||'', 자산코드: r['자산코드']||'', 명칭: r['명칭']||'', 위치: r['위치']||'', 관리자: r['관리자']||'', OS: r['OS']||'', 구매연월: r['구매연월']||r['구매일']||'', 금액: r['금액']||'', 납품업체: r['납품업체']||'', 품의서명: r['품의서명']||'', 비고: r['비고']||'', IP주소: '', MACaddress: '', HW사양: '' }));
} else if (sheetName === '구독SW') {
rows.forEach(r => data.subSw.push({ id: r['ID']||Math.random().toString(36).substring(2, 9), type: '구독SW', 분야: r['분야']||'', 법인: r['법인']||'', 부서: r['부서']||'', 제품명: r['제품명']||'', 구매: r['구매일']||'', 만료일: r['만료일']||'', 라이선스유형: r['라이선스유형']||'', 금액: r['금액']||'', 수량: parseInt(r['수량']||'1'), 계정명: r['계정명']||'', 납품업체: r['납품업체']||'', 비고: r['비고']||'' }));
rows.forEach(r => data.subSw.push({ id: r['ID']||Math.random().toString(36).substring(2, 9), type: '구독SW', 분야: r['분야']||'', 법인: r['법인']||'', 부서: r['부서']||'', 제품명: r['제품명']||'', 구매연월: r['구매연월']||r['구매일']||'', 만료일: r['만료일']||'', 라이선스유형: r['라이선스유형']||'', 금액: r['금액']||'', 수량: parseInt(r['수량']||'1'), 계정명: r['계정명']||'', 납품업체: r['납품업체']||'', 비고: r['비고']||'' }));
} else if (sheetName === '영구SW') {
rows.forEach(r => data.permSw.push({ id: r['ID']||Math.random().toString(36).substring(2, 9), type: '영구SW', 분야: r['분야']||'', 법인: r['법인']||'', 부서: r['부서']||'', 제품명: r['제품명']||'', 구매: r['구매일']||'', 라이선스키: r['라이선스키']||'', 금액: r['금액']||'', 수량: parseInt(r['수량']||'1'), 계정명: r['계정명']||'', 납품업체: r['납품업체']||'', 비고: r['비고']||'' }));
rows.forEach(r => data.permSw.push({ id: r['ID']||Math.random().toString(36).substring(2, 9), type: '영구SW', 분야: r['분야']||'', 법인: r['법인']||'', 부서: r['부서']||'', 제품명: r['제품명']||'', 구매연월: r['구매연월']||r['구매일']||'', 라이선스키: r['라이선스키']||'', 금액: r['금액']||'', 수량: parseInt(r['수량']||'1'), 계정명: r['계정명']||'', 납품업체: r['납품업체']||'', 비고: r['비고']||'' }));
}
});
resolve(data);

View File

@@ -609,7 +609,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "NAS",
"용도": "GSIM NAS",
@@ -629,7 +629,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "NAS",
"용도": "그래픽스개발팀 데이터 백업 NAS",
@@ -649,7 +649,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "PC",
"용도": "공통 GIT 서버",
@@ -669,7 +669,7 @@ export const realServerData = [
"SSD2": "1TB"
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "PC",
"용도": "BUILD 서버",
@@ -689,7 +689,7 @@ export const realServerData = [
"SSD2": "10TB"
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "PC",
"용도": "HmEG 테스트 서버",
@@ -709,7 +709,7 @@ export const realServerData = [
"SSD2": "1TB"
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "PC",
"용도": "산하 ERP 개발서버",
@@ -729,7 +729,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "PC",
"용도": "공간정보 신청",
@@ -749,7 +749,7 @@ export const realServerData = [
"SSD2": "931GB"
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "PC",
"용도": "AI 관련",
@@ -769,7 +769,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "PC",
"용도": "한종 테스트",
@@ -789,7 +789,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "PC",
"용도": "GSIM 언리얼 서버",
@@ -809,7 +809,7 @@ export const realServerData = [
"SSD2": "8TB"
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "PC",
"용도": "AutoCAD 테스트 서버",
@@ -829,7 +829,7 @@ export const realServerData = [
"SSD2": "2TB"
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "PC",
"용도": "GSIM 테스트 서버",
@@ -849,7 +849,7 @@ export const realServerData = [
"SSD2": "512GB"
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "PC",
"용도": "공간데이터 서버",
@@ -869,7 +869,7 @@ export const realServerData = [
"SSD2": "8 TB"
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "PC",
"용도": "가평 VM 원격 서버",
@@ -889,7 +889,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "서버",
"용도": "GSIM 협업",
@@ -909,7 +909,7 @@ export const realServerData = [
"SSD2": "1.88TB"
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "스토리지",
"용도": "GSIM 협업 스토리지",
@@ -929,7 +929,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "서버",
"용도": "GSIM META 서버",
@@ -949,7 +949,7 @@ export const realServerData = [
"SSD2": "4TB"
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "서버",
"용도": "GSIM 서버",
@@ -969,7 +969,7 @@ export const realServerData = [
"SSD2": "4TB"
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "스토리지",
"용도": "GSIM 스토리지",
@@ -989,7 +989,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "서버",
"용도": "함양-합천 서버",
@@ -1009,7 +1009,7 @@ export const realServerData = [
"SSD2": "10TB"
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "서버",
"용도": "HM MapService 2.0 서버",
@@ -1029,7 +1029,7 @@ export const realServerData = [
"SSD2": "40 TB"
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "스토리지",
"용도": "HM MapService 2.0 스토리지",
@@ -1049,7 +1049,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "서버",
"용도": "Gitlab Runner",
@@ -1069,7 +1069,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "기술개발센터",
"법인": "",
"자산코드": "",
"storage유형": "서버",
"용도": "전산모사",
@@ -1089,7 +1089,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "1",
"storage유형": "NAS",
"용도": "NAS 2",
@@ -1105,7 +1105,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "2",
"storage유형": "NAS",
"용도": "NAS 1",
@@ -1121,7 +1121,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "3",
"storage유형": "NAS",
"용도": "NAS 4",
@@ -1137,7 +1137,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "4",
"storage유형": "NAS",
"용도": "NAS 5",
@@ -1153,7 +1153,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "5",
"storage유형": "NAS",
"용도": "NAS 6",
@@ -1169,7 +1169,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "6",
"storage유형": "NAS",
"용도": "NAS7",
@@ -1185,7 +1185,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "7",
"storage유형": "NAS",
"용도": "총괄기획실 NAS",
@@ -1201,7 +1201,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "8",
"storage유형": "NAS",
"용도": "한맥 NAS 1",
@@ -1217,7 +1217,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "9",
"storage유형": "NAS",
"용도": "한맥 NAS 2",
@@ -1233,7 +1233,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "10",
"storage유형": "NAS",
"용도": "한맥 NAS 3",
@@ -1249,7 +1249,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "11",
"storage유형": "NAS",
"용도": "NAS 13",
@@ -1265,7 +1265,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "12",
"storage유형": "PC",
"용도": "회계",
@@ -1281,7 +1281,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "13",
"storage유형": "PC",
"용도": "한맥CAD",
@@ -1297,9 +1297,9 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "14",
"storage유형": "서버(타워)",
"storage유형": "PC",
"용도": "Ai-Cell-Util",
"상세": "깃티, 매터모스트 등 70여종",
"위치": "한맥빌딩(MDF 실)",
@@ -1313,7 +1313,7 @@ export const realServerData = [
"SSD2": "8 TB"
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "15",
"storage유형": "PC",
"용도": "한라CAD",
@@ -1329,7 +1329,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "16",
"storage유형": "NAS",
"용도": "디자인팀1 NAS",
@@ -1345,7 +1345,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "17",
"storage유형": "NAS",
"용도": "디자인팀2 NAS",
@@ -1361,9 +1361,9 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "18",
"storage유형": "서버(미니워크스테이션)",
"storage유형": "PC",
"용도": "인사정보 서버",
"상세": "인사정보 PM",
"위치": "한맥빌딩(MDF 실)",
@@ -1377,9 +1377,9 @@ export const realServerData = [
"SSD2": "2 TB"
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "19",
"storage유형": "서버(타워)",
"storage유형": "PC",
"용도": "BEPs 서버",
"상세": "BEPs 개발서버, Outline 협업서비스",
"위치": "한맥빌딩(MDF 실)",
@@ -1393,9 +1393,9 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "20",
"storage유형": "서버(타워)",
"storage유형": "PC",
"용도": "Ai-Cell-A100-1",
"상세": "OCR, Local LLM 등 30여종",
"위치": "한맥빌딩(MDF 실)",
@@ -1409,9 +1409,9 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "21",
"storage유형": "서버(타워)",
"storage유형": "PC",
"용도": "빌드서버",
"상세": "인스톨 쉴드, 지라",
"위치": "한맥빌딩(MDF 실)",
@@ -1425,9 +1425,9 @@ export const realServerData = [
"SSD2": "4TB"
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "22",
"storage유형": "PC\n서버(랙)",
"storage유형": "PC",
"용도": "저장소 및 전산모사\n구)스마트건설 서버",
"상세": "ParaView, CFDCore\n디지털화설문, 검색WIKI 웹서비스",
"위치": "한맥빌딩(MDF 실)",
@@ -1441,9 +1441,9 @@ export const realServerData = [
"SSD2": "2TB"
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "23",
"storage유형": "서버(랙)",
"storage유형": "서버",
"용도": "IDC 산하ERP서버",
"상세": "XR 가상화 메인 서버 → IDC 산하ERP서버",
"위치": "한맥빌딩(MDF 실)",
@@ -1457,9 +1457,9 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "24",
"storage유형": "스토리지(랙)",
"storage유형": "스토리지",
"용도": "WAS Storage",
"상세": "",
"위치": "한맥빌딩(MDF 실)",
@@ -1473,9 +1473,9 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "25",
"storage유형": "서버(랙)",
"storage유형": "서버",
"용도": "한맥 백업 서버",
"상세": "가족사 인트라넷 소스 백업 서버",
"위치": "한맥빌딩(MDF 실)",
@@ -1489,9 +1489,9 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "26",
"storage유형": "서버(랙)",
"storage유형": "서버",
"용도": "한라 백업 서버",
"상세": "한라 웹 소스 및 Miso DB 백업 서버",
"위치": "한맥빌딩(MDF 실)",
@@ -1505,7 +1505,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "27",
"storage유형": "NAS",
"용도": "기술개발센터 NAS",
@@ -1521,7 +1521,7 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "28",
"storage유형": "NAS",
"용도": "-",
@@ -1537,9 +1537,9 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "29",
"storage유형": "스토리지(랙)",
"storage유형": "스토리지",
"용도": "Backup Storage",
"상세": "",
"위치": "한맥빌딩(MDF 실)",
@@ -1553,9 +1553,9 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "30",
"storage유형": "스토리지(랙)",
"storage유형": "스토리지",
"용도": "-",
"상세": "",
"위치": "한맥빌딩(MDF 실)",
@@ -1569,9 +1569,9 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "31",
"storage유형": "서버(랙)",
"storage유형": "서버",
"용도": "XR WAS Server",
"상세": "",
"위치": "한맥빌딩(MDF 실)",
@@ -1585,9 +1585,9 @@ export const realServerData = [
"SSD2": ""
},
{
"법인": "한맥빌딩",
"법인": "",
"자산코드": "32",
"storage유형": "서버(랙)",
"storage유형": "서버",
"용도": "WAS Storage",
"상세": "",
"위치": "한맥빌딩(MDF 실)",

74
src/core/schema.ts Normal file
View File

@@ -0,0 +1,74 @@
/**
* ITAM 통합 스키마 매퍼 (Unified Schema Mapper)
*
* key: 애플리케이션 내부 로직에서 사용하는 속성명
* db: MySQL 데이터베이스 컬럼명
* ui: 사용자에게 보여지는 UI 레이블
*/
export const ASSET_SCHEMA = {
// ─── 공통 필드 (Common) ───
ID: { key: 'id', db: 'id', ui: 'ID' },
TYPE: { key: 'type', db: 'type', ui: '자산유형' },
CORP: { key: '법인', db: 'corp', ui: '구매법인' },
ASSET_CODE: { key: '자산코드', db: 'asset_code', ui: '자산번호' },
PURCHASE_YM: { key: '구매연월', db: 'purchase_date', ui: '구매연월' },
ORG: { key: '현사용조직', db: 'current_org', ui: '현 사용조직' },
PREV_ORG: { key: '이전사용조직', db: 'prev_org', ui: '이전 사용조직' },
LOCATION: { key: '위치', db: 'location', ui: '설치위치' },
MANAGER_MAIN: { key: '담당자_정', db: 'manager_main', ui: '담당자' },
MANAGER_SUB: { key: '담당자_부', db: 'manager_sub', ui: '담당자(부)' },
PRICE: { key: '금액', db: 'price', ui: '도입금액' },
VENDOR: { key: '납품업체', db: 'vendor', ui: '납품업체' },
DOC_NAME: { key: '품의서명', db: 'doc_name', ui: '품의서' },
REMARKS: { key: '비고', db: 'remarks', ui: '비고' },
// ─── 하드웨어 상세 (Hardware) ───
USER: { key: '사용자', db: 'purpose', ui: '사용자' },
MODEL: { key: '모델명', db: 'model_name', ui: '모델명' },
MAINBOARD: { key: '메인보드', db: 'mainboard', ui: '메인보드' },
OS: { key: 'OS', db: 'os', ui: '운영체제' },
CPU: { key: 'CPU', db: 'cpu', ui: 'CPU' },
RAM: { key: 'RAM', db: 'ram', ui: 'RAM' },
STORAGE1: { key: 'SSD1', db: 'storage1', ui: 'Storage 1' },
STORAGE2: { key: 'SSD2', db: 'storage2', ui: 'Storage 2' },
IP_ADDR: { key: 'IP주소', db: 'ip_address', ui: 'IP 주소 1' },
IP_ADDR2: { key: 'IP2', db: 'ip2', ui: 'IP 주소 2' },
MAC_ADDR: { key: 'MACaddress', db: 'mac_address', ui: 'MAC 주소' },
STATUS: { key: '현재상태', db: 'status', ui: '현재상태' },
STORE_LOC: { key: '보관위치', db: 'storage_location',ui: '보관위치' },
// ─── 소프트웨어/클라우드 상세 (SW/Cloud) ───
PRODUCT: { key: '제품명', db: 'product_name', ui: '제품/서비스명' },
PLATFORM: { key: '플랫폼명', db: 'platform_name', ui: '운영 플랫폼' },
LICENSE_TYPE: { key: '라이선스유형', db: 'license_type', ui: '라이선스 유형' },
LICENSE_KEY: { key: '라이선스키', db: 'license_key', ui: '라이선스 키' },
QTY: { key: '수량', db: 'quantity', ui: '보유수량' },
EXPIRY: { key: '만료일', db: 'expiry_date', ui: '만료/구독일' },
ACCOUNT: { key: '계정명', db: 'account_name', ui: '계정(이메일)' },
PAY_METHOD: { key: '결제수단', db: 'pay_method', ui: '결제수단' },
PAY_DAY: { key: '결제일', db: 'pay_day', ui: '결제일' },
CARD_NUM: { key: '연결카드번호', db: 'card_num', ui: '카드번호(뒷4자리)' },
BILLING: { key: '당월청구액', db: 'monthly_fee', ui: '당월 청구액' }
};
/**
* 용어 사전 (UI 텍스트 전용)
*/
export const UI_TEXT = {
ACTION: {
ADD: '신규 등록',
EDIT: '수정',
SAVE: '저장',
DELETE: '삭제',
CANCEL: '취소',
CLOSE: '닫기',
HISTORY_ADD: '이력 추가',
RESET_FILTER: '필터 초기화'
},
MESSAGES: {
CONFIRM_DELETE: '정말로 삭제하시겠습니까?',
SAVE_SUCCESS: '성공적으로 저장되었습니다.',
NO_DATA: '검색 결과가 없습니다.'
}
};

View File

@@ -14,15 +14,14 @@ export interface MasterAssetData {
logs: HardwareLog[];
// 동료 코드 호환용 통합 배열 (프론트엔드 로직용)
sw: SoftwareAsset[];
hw: HardwareAsset[];
sw: SoftwareAsset[];
}
export interface AppState {
activeCategory: 'dashboard' | 'hw' | 'sw' | 'ops';
activeSubTab: string;
activeCategory: 'dashboard' | 'hw' | 'sw';
activeSubTab: string; // '대시보드', '개인PC', '서버', '스토리지', '전산비품', '구독SW', '영구SW', '클라우드'
masterData: MasterAssetData;
activeCharts: any[];
}
// 초기 상태
@@ -38,12 +37,11 @@ export const state: AppState = {
subSw: [],
permSw: [],
cloud: [],
hw: [], // 호환용
sw: [], // 호환용
swUsers: [],
logs: [],
hw: []
},
activeCharts: []
logs: []
}
};
/**
@@ -52,16 +50,16 @@ export const state: AppState = {
export async function loadMasterDataFromDB() {
try {
const endpoints = [
{ key: 'pc', url: 'http://localhost:3000/api/pc' },
{ key: 'server', url: 'http://localhost:3000/api/server' },
{ key: 'storage', url: 'http://localhost:3000/api/storage' },
{ key: 'equip', url: 'http://localhost:3000/api/equip' },
{ key: 'mobile', url: 'http://localhost:3000/api/mobile' },
{ key: 'subSw', url: 'http://localhost:3000/api/sw/sub' },
{ key: 'permSw', url: 'http://localhost:3000/api/sw/perm' },
{ key: 'cloud', url: 'http://localhost:3000/api/cloud' },
{ key: 'swUsers', url: 'http://localhost:3000/api/sw-users' },
{ key: 'logs', url: 'http://localhost:3000/api/logs' }
{ key: 'pc', url: 'http://172.16.40.100:3000/api/pc' },
{ key: 'server', url: 'http://172.16.40.100:3000/api/server' },
{ key: 'storage', url: 'http://172.16.40.100:3000/api/storage' },
{ key: 'equip', url: 'http://172.16.40.100:3000/api/equip' },
{ key: 'mobile', url: 'http://172.16.40.100:3000/api/mobile' },
{ key: 'subSw', url: 'http://172.16.40.100:3000/api/sw/sub' },
{ key: 'permSw', url: 'http://172.16.40.100:3000/api/sw/perm' },
{ key: 'cloud', url: 'http://172.16.40.100:3000/api/cloud' },
{ key: 'swUsers', url: 'http://172.16.40.100:3000/api/sw-users' },
{ key: 'logs', url: 'http://172.16.40.100:3000/api/logs' }
];
const results = await Promise.all(endpoints.map(e => fetch(e.url)));
@@ -87,12 +85,14 @@ export async function loadMasterDataFromDB() {
}
}
// 동료 코드 호환을 위한 통합 sw/hw 배열 생성
// 동료 코드 호환을 위한 통합 sw 배열 생성
state.masterData.sw = [
...state.masterData.subSw,
...state.masterData.permSw,
...state.masterData.cloud
];
// 하드웨어 통합 배열 생성 (대시보드 등에서 사용)
state.masterData.hw = [
...state.masterData.pc,
...state.masterData.server,
@@ -121,18 +121,25 @@ export function saveHardwareAsset(updatedAsset: HardwareAsset) {
const type = updatedAsset.type || '';
const detailPurpose = (updatedAsset as any). || updatedAsset.detail_purpose || '';
// 1. 타겟 카테고리 결정 (유연한 검색)
// 1. 타겟 카테고리 결정 (사용자 정의 그룹 기준)
let targetKey: keyof MasterAssetData = 'equip';
if (type.includes('서버') || detailPurpose.includes('서버')) {
const upperType = type.toUpperCase();
const isServer = type.includes('서버') || detailPurpose.includes('서버');
const isStorage = ['NAS', 'DAS', '스토리지'].some(t => type.includes(t));
const isMobileGroup = ['모바일', '태블릿', '노트북', '휴대폰', '핸드폰'].some(t => type.includes(t));
const isEquipGroup = ['CPU', 'RAM', 'HDD', 'GPU'].some(t => upperType.includes(t));
const isPc = type === 'PC' || type === '개인PC' || detailPurpose === '개인PC';
if (isServer) {
targetKey = 'server';
} else if (['NAS', 'DAS', '스토리지'].some(t => type.includes(t))) {
} else if (isStorage) {
targetKey = 'storage';
} else if (['모바일', '태블릿', '휴대폰', '핸드폰', '노트북'].some(t => type.includes(t))) {
} else if (isMobileGroup) {
targetKey = 'mobile';
} else if (type === 'PC' || type === '개인PC' || detailPurpose === '개인PC') {
} else if (isPc) {
targetKey = 'pc';
} else if (['CPU', 'GPU', 'RAM', 'HDD'].some(t => type.toUpperCase().includes(t))) {
} else if (isEquipGroup) {
targetKey = 'equip';
}
@@ -148,6 +155,15 @@ export function saveHardwareAsset(updatedAsset: HardwareAsset) {
// 3. 새로운 타겟 카테고리에 추가
(state.masterData[targetKey] as HardwareAsset[]).push(updatedAsset);
// 4. 통합 hw 배열 동기화
state.masterData.hw = [
...state.masterData.pc,
...state.masterData.server,
...state.masterData.storage,
...state.masterData.equip,
...state.masterData.mobile
];
}
/**
@@ -162,4 +178,67 @@ export function deleteHardwareAsset(assetId: string) {
if (idx > -1) arr.splice(idx, 1);
}
});
// 통합 hw 배열 동기화
state.masterData.hw = [
...state.masterData.pc,
...state.masterData.server,
...state.masterData.storage,
...state.masterData.equip,
...state.masterData.mobile
];
}
/**
* 소프트웨어 자산 저장 (API 연동)
*/
export async function saveSoftwareAsset(asset: SoftwareAsset) {
try {
const response = await fetch('http://172.16.40.100:3000/api/software/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(asset)
});
if (response.ok) {
// 로컬 상태 업데이트
const key = asset.type === '구독SW' ? 'subSw' : (asset.type === '영구SW' ? 'permSw' : 'cloud');
const arr = state.masterData[key] as SoftwareAsset[];
const idx = arr.findIndex(a => a.id === asset.id);
if (idx > -1) arr[idx] = asset;
else arr.push(asset);
// 통합 sw 배열 동기화
state.masterData.sw = [...state.masterData.subSw, ...state.masterData.permSw, ...state.masterData.cloud];
return true;
}
} catch (err) {
console.error('SW 저장 실패:', err);
}
return false;
}
/**
* 소프트웨어 자산 삭제 (API 연동)
*/
export async function deleteSoftwareAsset(type: string, id: string) {
try {
const response = await fetch(`http://172.16.40.100:3000/api/asset/${type}/${id}`, {
method: 'DELETE'
});
if (response.ok) {
const key = type === '구독SW' ? 'subSw' : (type === '영구SW' ? 'permSw' : 'cloud');
const arr = state.masterData[key] as SoftwareAsset[];
const idx = arr.findIndex(a => a.id === id);
if (idx > -1) arr.splice(idx, 1);
// 통합 sw 배열 동기화
state.masterData.sw = [...state.masterData.subSw, ...state.masterData.permSw, ...state.masterData.cloud];
return true;
}
} catch (err) {
console.error('SW 삭제 실패:', err);
}
return false;
}

View File

@@ -15,8 +15,8 @@ export function formatPrice(value: string | number): string {
/**
* HTML 배지 생성 (정/부 담당자, 원격도구 등)
*/
export function createBadge(text: string, bgColor: string): string {
return `<span style="background:${bgColor}; color:white; font-size:10px; padding:1px 4px; border-radius:3px; font-weight:700; margin-right:4px; display:inline-block; line-height:1.2;">${text}</span>`;
export function createBadge(text: string, type: 'primary' | 'muted' | 'success' | 'danger' = 'primary'): string {
return `<span class="badge badge-${type}">${text}</span>`;
}
/**
@@ -33,6 +33,21 @@ export function normalizeDate(dateStr: string): string {
return (dateStr || '').replace(/\./g, '-').trim();
}
/**
* 구매일로부터 현재까지의 경과 연수 계산 (소수점 첫째자리)
*/
export function calculateAssetAge(purchaseDate: string): number {
const normalized = normalizeDate(purchaseDate);
if (!normalized) return 0;
const purchase = new Date(normalized);
if (isNaN(purchase.getTime())) return 0;
const diffMs = Date.now() - purchase.getTime();
const age = diffMs / (1000 * 60 * 60 * 24 * 365.25);
return Math.max(0, parseFloat(age.toFixed(1)));
}
/**
* 고유 ID 생성 (7자리 랜덤 문자열)
*/