feat: Update AssetTableView and DashboardView to improve software management UI
This commit is contained in:
@@ -76,17 +76,20 @@ function renderHwTable(table: HTMLTableElement, container: HTMLElement, mainCont
|
|||||||
|
|
||||||
function renderSwTable(table: HTMLTableElement, container: HTMLElement, mainContent: HTMLElement) {
|
function renderSwTable(table: HTMLTableElement, container: HTMLElement, mainContent: HTMLElement) {
|
||||||
const list = state.masterData.sw.filter(a => a.type === state.activeSubTab);
|
const list = state.masterData.sw.filter(a => a.type === state.activeSubTab);
|
||||||
table.innerHTML = `<thead><tr><th>No</th><th>법인</th><th>제품명</th><th>구매일</th><th>수량</th><th>사용가능</th><th>관리</th></tr></thead><tbody id="dynamic-tbody"></tbody>`;
|
const isSub = state.activeSubTab === '구독SW';
|
||||||
|
|
||||||
|
table.innerHTML = `<thead><tr><th>No</th><th>법인</th><th>제품명</th><th>구매일</th>${isSub ? '<th>구독일</th>' : ''}<th>수량</th><th>사용가능</th><th>관리</th></tr></thead><tbody id="dynamic-tbody"></tbody>`;
|
||||||
container.appendChild(table);
|
container.appendChild(table);
|
||||||
mainContent.appendChild(container);
|
mainContent.appendChild(container);
|
||||||
const tbody = document.getElementById('dynamic-tbody')!;
|
const tbody = document.getElementById('dynamic-tbody')!;
|
||||||
if (list.length === 0) { tbody.innerHTML = `<tr><td colspan="7">정보가 없습니다.</td></tr>`; return; }
|
if (list.length === 0) { tbody.innerHTML = `<tr><td colspan="${isSub ? 8 : 7}">정보가 없습니다.</td></tr>`; return; }
|
||||||
|
|
||||||
list.forEach((asset, idx) => {
|
list.forEach((asset, idx) => {
|
||||||
const assigned = state.masterData.swUsers.filter(u => u.swId === asset.id).length;
|
const assigned = state.masterData.swUsers.filter(u => u.swId === asset.id).length;
|
||||||
const avail = asset.수량 - assigned;
|
const avail = (typeof asset.수량 === 'number' ? asset.수량 : parseInt(asset.수량||'0', 10)) - assigned;
|
||||||
const tr = document.createElement('tr');
|
const tr = document.createElement('tr');
|
||||||
tr.style.cursor = 'pointer';
|
tr.style.cursor = 'pointer';
|
||||||
tr.innerHTML = `<td>${idx+1}</td><td>${asset.법인}</td><td>${asset.제품명}</td><td>${asset.구매일||''}</td><td>${asset.수량}</td><td><strong style="color: ${avail > 0 ? 'var(--primary)' : 'var(--danger)'}">${avail}</strong></td><td style="display:flex; gap:0.25rem;"><button class="btn-outline btn-edit">수정</button><button class="btn-outline btn-users"><i data-lucide="users" style="width:14px; height:14px;"></i></button></td>`;
|
tr.innerHTML = `<td>${idx+1}</td><td>${asset.법인}</td><td>${asset.제품명}</td><td>${asset.구매일||''}</td>${isSub ? `<td>${asset.구독일||''}</td>` : ''}<td>${asset.수량}</td><td><strong style="color: ${avail > 0 ? 'var(--primary)' : 'var(--danger)'}">${avail}</strong></td><td style="display:flex; gap:0.25rem;"><button class="btn-outline btn-edit">수정</button><button class="btn-outline btn-users"><i data-lucide="users" style="width:14px; height:14px;"></i></button></td>`;
|
||||||
tr.addEventListener('click', (e) => { if (!(e.target as HTMLElement).closest('button')) openSwModal(asset); });
|
tr.addEventListener('click', (e) => { if (!(e.target as HTMLElement).closest('button')) openSwModal(asset); });
|
||||||
tr.querySelector('.btn-edit')?.addEventListener('click', () => openSwModal(asset));
|
tr.querySelector('.btn-edit')?.addEventListener('click', () => openSwModal(asset));
|
||||||
tr.querySelector('.btn-users')?.addEventListener('click', () => openSwUserModal(asset));
|
tr.querySelector('.btn-users')?.addEventListener('click', () => openSwUserModal(asset));
|
||||||
|
|||||||
@@ -161,19 +161,39 @@ function renderSwDashboard(container: HTMLElement) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dashboard-layout-2col">
|
<div class="dashboard-layout-2col">
|
||||||
<div class="dashboard-card" data-action="sub-exp" style="padding: 1.25rem 1.5rem; cursor:pointer; display:flex; justify-content:space-between; align-items:center;">
|
<div class="dashboard-card" data-action="sub-exp" style="padding: 1.25rem 1.5rem; flex-direction:row; justify-content:space-between; align-items:center; cursor:pointer;">
|
||||||
<div>
|
<div style="flex:1;">
|
||||||
<span style="font-size:1rem; font-weight:700; color:var(--text-main);">구독 SW 만료 예정</span>
|
<div style="display:flex; align-items:center; gap: 0.5rem; margin-bottom: 0.5rem;">
|
||||||
<div style="font-size: 0.8125rem; color:var(--text-muted);">${subExp}개 만료 예정</div>
|
<span style="font-size:1rem; font-weight:700; color:var(--text-main);">구독 SW 만료 예정</span>
|
||||||
|
<span style="font-size:0.75rem; color:#bfbfbf; background:#f9f9f9; padding:2px 6px; border-radius:4px;">30일 이내</span>
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 0.8125rem; color:var(--text-muted); margin-bottom: 1.25rem;">
|
||||||
|
전체 ${subTotal}개 제품 중 ${subExp}개 만료 예정
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 1.5rem; font-weight:700; color:${subExp > 0 ? 'var(--dash-danger)' : 'var(--text-main)'};">${subExp}개</div>
|
||||||
|
</div>
|
||||||
|
<div style="width: 80px; height: 80px; border-radius: 50%; background: conic-gradient(var(--dash-danger) ${subExpPer}%, var(--border-color) 0); display:flex; justify-content:center; align-items:center;">
|
||||||
|
<div style="width: 64px; height: 64px; border-radius: 50%; background: var(--white); display:flex; justify-content:center; align-items:center;">
|
||||||
|
<span style="font-size: 1rem; color:var(--text-muted); font-weight:600;">${subExpPer}%</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="width: 60px; height: 60px; border-radius: 50%; background: conic-gradient(var(--dash-danger) ${subExpPer}%, var(--border-color) 0);"></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="dashboard-card" data-action="perm-exp" style="padding: 1.25rem 1.5rem; cursor:pointer; display:flex; justify-content:space-between; align-items:center;">
|
<div class="dashboard-card" data-action="perm-exp" style="padding: 1.25rem 1.5rem; flex-direction:row; justify-content:space-between; align-items:center; cursor:pointer;">
|
||||||
<div>
|
<div style="flex:1;">
|
||||||
<span style="font-size:1rem; font-weight:700; color:var(--text-main);">유지보수 만료 예정</span>
|
<div style="display:flex; align-items:center; gap: 0.5rem; margin-bottom: 0.5rem;">
|
||||||
<div style="font-size: 0.8125rem; color:var(--text-muted);">${permExp}개 만료 예정</div>
|
<span style="font-size:1rem; font-weight:700; color:var(--text-main);">유지보수 만료 예정</span>
|
||||||
|
<span style="font-size:0.75rem; color:#bfbfbf; background:#f9f9f9; padding:2px 6px; border-radius:4px;">30일 이내</span>
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 0.8125rem; color:var(--text-muted); margin-bottom: 1.25rem;">
|
||||||
|
전체 ${permTotal}개 제품 중 ${permExp}개 만료 예정
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 1.5rem; font-weight:700; color:${permExp > 0 ? 'var(--dash-danger)' : 'var(--text-main)'};">${permExp}개</div>
|
||||||
|
</div>
|
||||||
|
<div style="width: 80px; height: 80px; border-radius: 50%; background: conic-gradient(var(--dash-danger) ${permExpPer}%, var(--border-color) 0); display:flex; justify-content:center; align-items:center;">
|
||||||
|
<div style="width: 64px; height: 64px; border-radius: 50%; background: var(--white); display:flex; justify-content:center; align-items:center;">
|
||||||
|
<span style="font-size: 1rem; color:var(--text-muted); font-weight:600;">${permExpPer}%</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="width: 60px; height: 60px; border-radius: 50%; background: conic-gradient(var(--dash-danger) ${permExpPer}%, var(--border-color) 0);"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
Reference in New Issue
Block a user