Compare commits
1 Commits
9fcecd4bf5
...
Operation_
| Author | SHA1 | Date | |
|---|---|---|---|
| 367f72673d |
17
db_init.js
17
db_init.js
@@ -149,6 +149,23 @@ async function initDB() {
|
|||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
await connection.query(`
|
||||||
|
CREATE TABLE ops_domain_assets (
|
||||||
|
id VARCHAR(50) PRIMARY KEY,
|
||||||
|
type VARCHAR(50) COMMENT '유형',
|
||||||
|
corp VARCHAR(100) COMMENT '법인',
|
||||||
|
service_name VARCHAR(255) COMMENT '서비스명',
|
||||||
|
domain_name VARCHAR(255) COMMENT '관리도메인',
|
||||||
|
start_date VARCHAR(50) COMMENT '시작일',
|
||||||
|
expiry_date VARCHAR(50) COMMENT '만료일',
|
||||||
|
price VARCHAR(100) COMMENT '금액',
|
||||||
|
manager_main VARCHAR(100) COMMENT '담당자',
|
||||||
|
manager_sub VARCHAR(100) COMMENT '담당자(부)',
|
||||||
|
remarks TEXT COMMENT '비고',
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
`);
|
||||||
|
|
||||||
console.log('✅ 모든 테이블이 영문 표준 스키마로 재생성되었습니다.');
|
console.log('✅ 모든 테이블이 영문 표준 스키마로 재생성되었습니다.');
|
||||||
await connection.end();
|
await connection.end();
|
||||||
}
|
}
|
||||||
|
|||||||
26
server.js
26
server.js
@@ -93,6 +93,14 @@ async function ensureTables() {
|
|||||||
position VARCHAR(100), user_name VARCHAR(100), usage_period VARCHAR(255), doc_name VARCHAR(255)
|
position VARCHAR(100), user_name VARCHAR(100), usage_period VARCHAR(255), doc_name VARCHAR(255)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
`);
|
`);
|
||||||
|
await connection.query(`
|
||||||
|
CREATE TABLE IF NOT EXISTS ops_domain_assets (
|
||||||
|
id VARCHAR(50) PRIMARY KEY, type VARCHAR(50), corp VARCHAR(100),
|
||||||
|
service_name VARCHAR(255), domain_name VARCHAR(255), start_date VARCHAR(50),
|
||||||
|
expiry_date VARCHAR(50), price VARCHAR(100), manager_main VARCHAR(100),
|
||||||
|
manager_sub VARCHAR(100), remarks TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
`);
|
||||||
|
|
||||||
console.log('✅ All ITAM tables ensured.');
|
console.log('✅ All ITAM tables ensured.');
|
||||||
} finally {
|
} finally {
|
||||||
@@ -405,6 +413,24 @@ app.post('/api/sw-users/batch', async (req, res) => {
|
|||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 도메인 관리 API
|
||||||
|
app.get('/api/ops/domain', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const [rows] = await pool.query('SELECT * FROM ops_domain_assets ORDER BY created_at DESC');
|
||||||
|
res.json(rows);
|
||||||
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/api/ops/domain/batch', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const result = await batchSave('ops_domain_assets', req.body, (assets) => ({
|
||||||
|
sql: `INSERT INTO ops_domain_assets (id, type, corp, service_name, domain_name, start_date, expiry_date, price, manager_main, manager_sub, remarks) VALUES ?`,
|
||||||
|
values: assets.map(a => [a.id, a.type||'', a.corp||'', a.service_name||'', a.domain_name||'', a.start_date||'', a.expiry_date||'', a.price||'', a.manager_main||'', a.manager_sub||'', a.remarks||''])
|
||||||
|
}));
|
||||||
|
res.json(result);
|
||||||
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
|
});
|
||||||
|
|
||||||
// 자산번호 자동 생성 API
|
// 자산번호 자동 생성 API
|
||||||
app.get('/api/generate-asset-code', async (req, res) => {
|
app.get('/api/generate-asset-code', async (req, res) => {
|
||||||
const { prefix } = req.query;
|
const { prefix } = req.query;
|
||||||
|
|||||||
192
src/components/Modal/DomainModal.ts
Normal file
192
src/components/Modal/DomainModal.ts
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
import { state } from '../../core/state';
|
||||||
|
import { closeModals, openModal } from './BaseModal';
|
||||||
|
import { CORP_LIST } from './SharedData';
|
||||||
|
import { generateOptionsHTML } from './ModalUtils';
|
||||||
|
import { createIcons, X, Save, Database, CalendarClock, Edit2 } from 'lucide';
|
||||||
|
|
||||||
|
let currentItem: any = null;
|
||||||
|
|
||||||
|
const DOMAIN_MODAL_HTML = `
|
||||||
|
<div id="domain-asset-modal" class="modal-overlay hidden">
|
||||||
|
<div class="modal-content wide">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2 id="domain-modal-title">도메인 정보</h2>
|
||||||
|
<div style="display:flex; gap:0.5rem; align-items:center;">
|
||||||
|
<button id="btn-close-domain-modal" class="btn-icon"><i data-lucide="x"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="modal-form-area">
|
||||||
|
<form id="domain-asset-form" class="grid-form">
|
||||||
|
|
||||||
|
<!-- Group 1: 기본 정보 (Service Identity) -->
|
||||||
|
<div class="form-section-title" style="display:flex; align-items:center; gap:0.5rem;">
|
||||||
|
<i data-lucide="database" style="width:16px; height:16px; color:var(--primary-color);"></i>
|
||||||
|
기본 정보 (Identity)
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="required">유형</label>
|
||||||
|
<select id="domain-type" required>
|
||||||
|
<option value="호스팅">호스팅</option>
|
||||||
|
<option value="SSL">SSL</option>
|
||||||
|
<option value="도메인">도메인</option>
|
||||||
|
<option value="네임서버">네임서버</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="required">법인</label>
|
||||||
|
<select id="domain-corp" required>
|
||||||
|
${generateOptionsHTML(CORP_LIST)}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="required">서비스명</label>
|
||||||
|
<input type="text" id="domain-service-name" placeholder="예: 그룹웨어, 홈페이지" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="required">관리도메인</label>
|
||||||
|
<input type="text" id="domain-name" placeholder="예: hmac.kr" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Group 2: 계약 및 담당 정보 (Contract & Manager) -->
|
||||||
|
<div class="form-section-title" style="display:flex; align-items:center; gap:0.5rem; margin-top:1.5rem;">
|
||||||
|
<i data-lucide="calendar-clock" style="width:16px; height:16px; color:var(--primary-color);"></i>
|
||||||
|
계약 및 담당 정보
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>계약 시작일</label>
|
||||||
|
<input type="date" id="domain-start-date">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>계약 만료일</label>
|
||||||
|
<input type="date" id="domain-expiry-date">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>도입 금액</label>
|
||||||
|
<input type="text" id="domain-price" oninput="this.value = this.value.replace(/[^0-9]/g, '').replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',')" placeholder="0">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>담당자</label>
|
||||||
|
<input type="text" id="domain-manager-main">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>담당자(부)</label>
|
||||||
|
<input type="text" id="domain-manager-sub">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Group 3: 기타 (Additional) -->
|
||||||
|
<div class="form-section-title" style="display:flex; align-items:center; gap:0.5rem; margin-top:1.5rem;">
|
||||||
|
<i data-lucide="edit-2" style="width:16px; height:16px; color:var(--primary-color);"></i>
|
||||||
|
기타 사항
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group full-width">
|
||||||
|
<label>비고</label>
|
||||||
|
<textarea id="domain-remarks" rows="3" style="width:100%; border:1px solid var(--border-color); border-radius:4px; padding:0.625rem;"></textarea>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button id="btn-cancel-domain" class="btn btn-outline">취소</button>
|
||||||
|
<button id="btn-save-domain" class="btn btn-primary"><i data-lucide="save"></i> 저장하기</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
export function initDomainModal() {
|
||||||
|
if (!document.getElementById('domain-asset-modal')) {
|
||||||
|
document.body.insertAdjacentHTML('beforeend', DOMAIN_MODAL_HTML);
|
||||||
|
}
|
||||||
|
|
||||||
|
const modal = document.getElementById('domain-asset-modal')!;
|
||||||
|
document.getElementById('btn-close-domain-modal')?.addEventListener('click', () => closeModals());
|
||||||
|
document.getElementById('btn-cancel-domain')?.addEventListener('click', () => closeModals());
|
||||||
|
document.getElementById('btn-save-domain')?.addEventListener('click', () => saveDomain());
|
||||||
|
}
|
||||||
|
|
||||||
|
export function openDomainModal(item: any = null) {
|
||||||
|
currentItem = item;
|
||||||
|
const isEdit = !!item;
|
||||||
|
|
||||||
|
const titleEl = document.getElementById('domain-modal-title');
|
||||||
|
if (titleEl) titleEl.textContent = isEdit ? '도메인 정보 수정' : '신규 도메인 등록';
|
||||||
|
|
||||||
|
const setVal = (id: string, val: any) => {
|
||||||
|
const el = document.getElementById(id) as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
|
||||||
|
if (el) el.value = val || '';
|
||||||
|
};
|
||||||
|
|
||||||
|
setVal('domain-type', item?.type || '호스팅');
|
||||||
|
setVal('domain-corp', item?.corp || '');
|
||||||
|
setVal('domain-service-name', item?.service_name || '');
|
||||||
|
setVal('domain-name', item?.domain_name || '');
|
||||||
|
setVal('domain-start-date', item?.start_date || '');
|
||||||
|
setVal('domain-expiry-date', item?.expiry_date || '');
|
||||||
|
setVal('domain-price', item?.price || '');
|
||||||
|
setVal('domain-manager-main', item?.manager_main || '');
|
||||||
|
setVal('domain-manager-sub', item?.manager_sub || '');
|
||||||
|
setVal('domain-remarks', item?.remarks || '');
|
||||||
|
|
||||||
|
openModal('domain-asset-modal');
|
||||||
|
createIcons({ icons: { X, Save, Database, CalendarClock, Edit2 } });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveDomain() {
|
||||||
|
const getVal = (id: string) => (document.getElementById(id) as HTMLInputElement)?.value || '';
|
||||||
|
|
||||||
|
const newDomain = {
|
||||||
|
id: currentItem ? currentItem.id : `DOM-${Date.now()}`,
|
||||||
|
type: getVal('domain-type'),
|
||||||
|
corp: getVal('domain-corp'),
|
||||||
|
service_name: getVal('domain-service-name'),
|
||||||
|
domain_name: getVal('domain-name'),
|
||||||
|
start_date: getVal('domain-start-date'),
|
||||||
|
expiry_date: getVal('domain-expiry-date'),
|
||||||
|
price: getVal('domain-price'),
|
||||||
|
manager_main: getVal('domain-manager-main'),
|
||||||
|
manager_sub: getVal('domain-manager-sub'),
|
||||||
|
remarks: getVal('domain-remarks')
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!newDomain.service_name || !newDomain.domain_name) {
|
||||||
|
alert('서비스명과 관리도메인은 필수 입력 사항입니다.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentItem) {
|
||||||
|
const idx = state.masterData.domain.findIndex(d => d.id === currentItem.id);
|
||||||
|
if (idx > -1) state.masterData.domain[idx] = newDomain;
|
||||||
|
} else {
|
||||||
|
state.masterData.domain.push(newDomain);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`http://${location.hostname}:3000/api/ops/domain/batch`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(state.masterData.domain)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
// alert('성공적으로 저장되었습니다.');
|
||||||
|
closeModals();
|
||||||
|
window.dispatchEvent(new CustomEvent('refresh-view'));
|
||||||
|
} else {
|
||||||
|
throw new Error('DB 저장 실패');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
alert('저장 중 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ export interface MasterAssetData {
|
|||||||
cloud: SoftwareAsset[]; // 클라우드 배열 추가
|
cloud: SoftwareAsset[]; // 클라우드 배열 추가
|
||||||
swUsers: SWUser[];
|
swUsers: SWUser[];
|
||||||
logs: HardwareLog[];
|
logs: HardwareLog[];
|
||||||
|
domain: any[];
|
||||||
|
|
||||||
// 동료 코드 호환용 통합 배열 (프론트엔드 로직용)
|
// 동료 코드 호환용 통합 배열 (프론트엔드 로직용)
|
||||||
hw: HardwareAsset[];
|
hw: HardwareAsset[];
|
||||||
@@ -41,7 +42,8 @@ export const state: AppState = {
|
|||||||
hw: [], // 호환용
|
hw: [], // 호환용
|
||||||
sw: [], // 호환용
|
sw: [], // 호환용
|
||||||
swUsers: [],
|
swUsers: [],
|
||||||
logs: []
|
logs: [],
|
||||||
|
domain: []
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -59,6 +61,7 @@ export async function loadMasterDataFromDB() {
|
|||||||
{ key: 'subSw', url: `http://${location.hostname}:3000/api/sw/sub` },
|
{ key: 'subSw', url: `http://${location.hostname}:3000/api/sw/sub` },
|
||||||
{ key: 'permSw', url: `http://${location.hostname}:3000/api/sw/perm` },
|
{ key: 'permSw', url: `http://${location.hostname}:3000/api/sw/perm` },
|
||||||
{ key: 'cloud', url: `http://${location.hostname}:3000/api/cloud` },
|
{ key: 'cloud', url: `http://${location.hostname}:3000/api/cloud` },
|
||||||
|
{ key: 'domain', url: `http://${location.hostname}:3000/api/ops/domain` },
|
||||||
{ key: 'swUsers', url: `http://${location.hostname}:3000/api/sw-users` },
|
{ key: 'swUsers', url: `http://${location.hostname}:3000/api/sw-users` },
|
||||||
{ key: 'logs', url: `http://${location.hostname}:3000/api/logs` }
|
{ key: 'logs', url: `http://${location.hostname}:3000/api/logs` }
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { initBaseModal } from './components/Modal/BaseModal';
|
|||||||
import { initHwModal, openHwModal } from './components/Modal/HWModal';
|
import { initHwModal, openHwModal } from './components/Modal/HWModal';
|
||||||
import { initSwModal, openSwModal } from './components/Modal/SWModal';
|
import { initSwModal, openSwModal } from './components/Modal/SWModal';
|
||||||
import { initSwUserModal } from './components/Modal/SWUserModal';
|
import { initSwUserModal } from './components/Modal/SWUserModal';
|
||||||
|
import { initDomainModal, openDomainModal } from './components/Modal/DomainModal';
|
||||||
import { initDashboardDetailModal } from './components/Modal/DashboardDetailModal';
|
import { initDashboardDetailModal } from './components/Modal/DashboardDetailModal';
|
||||||
import { initGuide } from './components/Guide';
|
import { initGuide } from './components/Guide';
|
||||||
import { createIcons, Download, Upload, FileSpreadsheet, Plus, X, LayoutDashboard, Monitor, Server, Database, Laptop, CalendarClock, Key, Cpu, Layers, Users, Paperclip, Edit2, History, RefreshCcw, BookOpen, Settings } from 'lucide';
|
import { createIcons, Download, Upload, FileSpreadsheet, Plus, X, LayoutDashboard, Monitor, Server, Database, Laptop, CalendarClock, Key, Cpu, Layers, Users, Paperclip, Edit2, History, RefreshCcw, BookOpen, Settings } from 'lucide';
|
||||||
@@ -109,6 +110,7 @@ function initApp() {
|
|||||||
}, closeAllModals);
|
}, closeAllModals);
|
||||||
|
|
||||||
initDashboardDetailModal();
|
initDashboardDetailModal();
|
||||||
|
initDomainModal();
|
||||||
initGuide();
|
initGuide();
|
||||||
|
|
||||||
// DB 데이터 로드 및 초기 화면 렌더링
|
// DB 데이터 로드 및 초기 화면 렌더링
|
||||||
@@ -142,6 +144,8 @@ function initApp() {
|
|||||||
openHwModal({ id: Math.random().toString(36).substring(2, 9), type: defaultType, 법인: '한맥', 자산코드: '', 명칭: '', 설치위치: '', MACaddress: '', HW사양: '', OS: '', 연락처: '', 담당부서: '' } as any, 'add');
|
openHwModal({ id: Math.random().toString(36).substring(2, 9), type: defaultType, 법인: '한맥', 자산코드: '', 명칭: '', 설치위치: '', MACaddress: '', HW사양: '', OS: '', 연락처: '', 담당부서: '' } as any, 'add');
|
||||||
} else if (cat === 'sw') {
|
} else if (cat === 'sw') {
|
||||||
openSwModal({ id: Math.random().toString(36).substring(2, 9), type: tab === '대시보드' ? '구독SW' : tab, 제품명: '', 금액: '', 수량: 1, 계정명: '', 납품업체: '', 비고: '', 법인: '한맥' } as any, 'add');
|
openSwModal({ id: Math.random().toString(36).substring(2, 9), type: tab === '대시보드' ? '구독SW' : tab, 제품명: '', 금액: '', 수량: 1, 계정명: '', 납품업체: '', 비고: '', 법인: '한맥' } as any, 'add');
|
||||||
|
} else if (cat === 'ops') {
|
||||||
|
if (tab === '도메인') openDomainModal(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -269,8 +269,7 @@ body {
|
|||||||
/* --- Layout Frame --- */
|
/* --- Layout Frame --- */
|
||||||
.content-area {
|
.content-area {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 0 2rem;
|
padding: 1.25rem 2rem 0; /* 상단 여백 1.25rem 추가 */
|
||||||
/* 좌우 여백만 유지 */
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
/* 전체 스크롤 차단 */
|
/* 전체 스크롤 차단 */
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
74
src/views/List/DomainListView.ts
Normal file
74
src/views/List/DomainListView.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import { state } from '../../core/state';
|
||||||
|
import { formatPrice } from '../../core/utils';
|
||||||
|
import { createIcons, Plus, Edit2, Trash2 } from 'lucide';
|
||||||
|
import { openDomainModal } from '../../components/Modal/DomainModal';
|
||||||
|
|
||||||
|
export function renderDomainList(container: HTMLElement) {
|
||||||
|
container.innerHTML = '';
|
||||||
|
|
||||||
|
const header = document.createElement('div');
|
||||||
|
header.className = 'list-header';
|
||||||
|
header.innerHTML = `
|
||||||
|
<div class="list-title-area">
|
||||||
|
<h2 class="list-title">도메인 관리</h2>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
container.appendChild(header);
|
||||||
|
|
||||||
|
const tableWrapper = document.createElement('div');
|
||||||
|
tableWrapper.className = 'table-container';
|
||||||
|
|
||||||
|
const table = document.createElement('table');
|
||||||
|
table.innerHTML = `
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="text-align:center; width:50px;">No.</th>
|
||||||
|
<th style="text-align:center;">유형</th>
|
||||||
|
<th style="text-align:center;">법인</th>
|
||||||
|
<th style="text-align:left;">서비스명</th>
|
||||||
|
<th style="text-align:left;">관리도메인</th>
|
||||||
|
<th style="text-align:center;">시작일</th>
|
||||||
|
<th style="text-align:center;">만료일</th>
|
||||||
|
<th style="text-align:right;">금액</th>
|
||||||
|
<th style="text-align:center;">담당자</th>
|
||||||
|
<th style="text-align:center;">담당자(부)</th>
|
||||||
|
<th style="text-align:left;">비고</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
${state.masterData.domain.length === 0 ? `
|
||||||
|
<tr>
|
||||||
|
<td colspan="11" style="text-align:center; padding: 3rem; color: var(--text-muted);">등록된 도메인 정보가 없습니다.</td>
|
||||||
|
</tr>
|
||||||
|
` : state.masterData.domain.map((item, idx) => `
|
||||||
|
<tr class="domain-row" data-id="${item.id}" style="cursor:pointer;">
|
||||||
|
<td style="text-align:center;">${idx + 1}</td>
|
||||||
|
<td style="text-align:center;"><span class="badge badge-${item.type}">${item.type}</span></td>
|
||||||
|
<td style="text-align:center;">${item.corp || ''}</td>
|
||||||
|
<td>${item.service_name || ''}</td>
|
||||||
|
<td>${item.domain_name || ''}</td>
|
||||||
|
<td style="text-align:center;">${item.start_date || ''}</td>
|
||||||
|
<td style="text-align:center;">${item.expiry_date || ''}</td>
|
||||||
|
<td style="text-align:right;">${formatPrice(item.price)}</td>
|
||||||
|
<td style="text-align:center;">${item.manager_main || ''}</td>
|
||||||
|
<td style="text-align:center;">${item.manager_sub || ''}</td>
|
||||||
|
<td class="text-truncate" style="max-width:200px;">${item.remarks || ''}</td>
|
||||||
|
</tr>
|
||||||
|
`).join('')}
|
||||||
|
</tbody>
|
||||||
|
`;
|
||||||
|
|
||||||
|
tableWrapper.appendChild(table);
|
||||||
|
container.appendChild(tableWrapper);
|
||||||
|
|
||||||
|
// 이벤트 바인딩
|
||||||
|
table.querySelectorAll('.domain-row').forEach(row => {
|
||||||
|
row.addEventListener('click', () => {
|
||||||
|
const id = row.getAttribute('data-id');
|
||||||
|
const item = state.masterData.domain.find(d => d.id === id);
|
||||||
|
if (item) openDomainModal(item);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
createIcons({ icons: { Plus, Edit2, Trash2 } });
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import { renderEquipmentList } from './List/EquipmentListView';
|
|||||||
import { renderMobileList } from './List/MobileListView';
|
import { renderMobileList } from './List/MobileListView';
|
||||||
import { renderSwList } from './List/SwListView';
|
import { renderSwList } from './List/SwListView';
|
||||||
import { renderCloudList } from './List/CloudListView';
|
import { renderCloudList } from './List/CloudListView';
|
||||||
|
import { renderDomainList } from './List/DomainListView';
|
||||||
import { createIcons, Download, Upload, FileSpreadsheet, Plus, X, LayoutDashboard, Monitor, Server, Database, Laptop, CalendarClock, Key, Cpu, Layers, Users, Paperclip, Edit2, RefreshCcw } from 'lucide';
|
import { createIcons, Download, Upload, FileSpreadsheet, Plus, X, LayoutDashboard, Monitor, Server, Database, Laptop, CalendarClock, Key, Cpu, Layers, Users, Paperclip, Edit2, RefreshCcw } from 'lucide';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,10 +41,9 @@ export function renderSWTable(mainContent: HTMLElement) {
|
|||||||
container.innerHTML = `<div style="padding:2rem; color:var(--text-muted);">"${tab}" 탭에 대한 소프트웨어 리스트 뷰가 정의되지 않았습니다.</div>`;
|
container.innerHTML = `<div style="padding:2rem; color:var(--text-muted);">"${tab}" 탭에 대한 소프트웨어 리스트 뷰가 정의되지 않았습니다.</div>`;
|
||||||
}
|
}
|
||||||
} else if (state.activeCategory === 'ops') {
|
} else if (state.activeCategory === 'ops') {
|
||||||
if (['도메인', '메일', '메신저', '청구비용'].includes(tab)) {
|
if (tab === '도메인') renderDomainList(container);
|
||||||
renderCloudList(container);
|
else {
|
||||||
} else {
|
container.innerHTML = `<div style="padding:2rem; color:var(--text-muted); text-align:center; margin-top:3rem;">운영 서비스(${tab}) 관리 기능은 현재 준비 중입니다.</div>`;
|
||||||
container.innerHTML = `<div style="padding:2rem; color:var(--text-muted);">"${tab}" 탭에 대한 운영 서비스 뷰가 정의되지 않았습니다.</div>`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user