fix(audit): 실사 승인 대기 목록 필터링 오류 수정 및 승인완료 배지 표시 추가
- GET /api/audit/pending API에서 PENDING 상태인 내역만 반환하도록 SQL 쿼리 수정 - 실사 승인 및 맵 저장 시 asset_location.location_detail에 표준 상세 위치가 저장되도록 개선 - 실사 승인 완료된 자산에 대해 상세 모달, 위치 보기, 목록 보기에 '승인완료' 배지 노출 처리 - 목록 보기에서 기존 위치 표시 형식 및 툴팁을 훼손하지 않도록 배지를 분리하여 렌더링
This commit is contained in:
12
server.js
12
server.js
@@ -143,6 +143,9 @@ app.get('/api/assets/master', async (req, res) => {
|
||||
s.hw_status, s.model_name, s.mainboard, s.os, s.cpu, s.ram, s.gpu,
|
||||
s.monitoring, s.price, s.monitor_inch, s.serial_num,
|
||||
l.location, l.location_detail, l.location_photo, l.loc_x, l.loc_y,
|
||||
(
|
||||
SELECT EXISTS(SELECT 1 FROM asset_audit_pending WHERE asset_code = c.asset_code AND status = 'APPROVED')
|
||||
) AS is_audit_approved,
|
||||
(
|
||||
SELECT JSON_ARRAYAGG(JSON_OBJECT('type', net_type, 'name', net_name, 'val1', net_value1, 'val2', net_value2))
|
||||
FROM asset_remote WHERE asset_id = c.id AND is_active = 1
|
||||
@@ -786,11 +789,13 @@ app.post('/api/maps/save', async (req, res) => {
|
||||
);
|
||||
|
||||
// Insert new active location mapping
|
||||
const pathPartsForMap = path.split('/');
|
||||
const stdDetailForMap = pathPartsForMap[pathPartsForMap.length - 2] || locDetail;
|
||||
await connection.query(`
|
||||
INSERT INTO asset_location
|
||||
(asset_id, location, location_detail, location_photo, loc_x, loc_y, physical_location_code, is_active)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, 1)
|
||||
`, [box.asset_id, locName, locDetail, path, box.x, box.y, locCode]);
|
||||
`, [box.asset_id, locName, stdDetailForMap, path, box.x, box.y, locCode]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -867,6 +872,7 @@ app.get('/api/audit/pending', async (req, res) => {
|
||||
JOIN asset_core c ON c.asset_code = ap.asset_code
|
||||
JOIN physical_locations pl ON pl.location_code = ap.physical_location_code
|
||||
LEFT JOIN asset_location l ON l.asset_id = c.id AND l.is_active = 1
|
||||
WHERE ap.status = 'PENDING'
|
||||
ORDER BY ap.scanned_at DESC
|
||||
`);
|
||||
res.json(rows);
|
||||
@@ -923,11 +929,13 @@ app.post('/api/audit/approve', async (req, res) => {
|
||||
);
|
||||
|
||||
// 5. Insert new active location
|
||||
const pathPartsForApprove = loc.map_image.split('/');
|
||||
const stdDetailForApprove = pathPartsForApprove[pathPartsForApprove.length - 2] || loc.location_detail;
|
||||
await connection.query(`
|
||||
INSERT INTO asset_location
|
||||
(asset_id, location, location_detail, location_photo, loc_x, loc_y, physical_location_code, is_active)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, 1)
|
||||
`, [assetId, loc.location_name, loc.location_detail, loc.map_image, loc.map_x, loc.map_y, physical_location_code]);
|
||||
`, [assetId, loc.location_name, stdDetailForApprove, loc.map_image, loc.map_x, loc.map_y, physical_location_code]);
|
||||
|
||||
// 6. Update pending audit status
|
||||
await connection.query(
|
||||
|
||||
@@ -35,6 +35,7 @@ class HwAssetModal extends BaseModal {
|
||||
<h2 id="hw-modal-title" class="modal-title" style="display: none;">${this.title}</h2>
|
||||
<div id="hw-header-identity" class="header-identity" style="display: inline-flex; gap: 0.5rem; align-items: center;"></div>
|
||||
<button id="btn-print-hw-qr" class="btn btn-outline btn-primary hidden" style="padding: 2px 8px; font-size: 11px; height: 22px; margin: 0; line-height: 1; display: inline-flex; align-items: center; justify-content: center; cursor: pointer;">QR 인쇄</button>
|
||||
<span id="hw-modal-audit-approved-badge" style="display: none; align-items: center; background-color: rgba(16, 185, 129, 0.08); color: #059669; border: 1px solid rgba(16, 185, 129, 0.18); padding: 1px 6px; border-radius: 4px; font-size: 10px; font-weight: 600; height: 20px; line-height: 1; vertical-align: middle; white-space: nowrap; margin-left: 4px;">승인완료</span>
|
||||
</div>
|
||||
<button id="btn-close-hw-modal" class="btn-icon" aria-label="닫기">×</button>
|
||||
</div>
|
||||
@@ -666,6 +667,12 @@ class HwAssetModal extends BaseModal {
|
||||
qrBtn.classList.toggle('hidden', mode !== 'view' || !hasCode);
|
||||
}
|
||||
|
||||
const approvedBadge = document.getElementById('hw-modal-audit-approved-badge');
|
||||
if (approvedBadge) {
|
||||
const isApproved = asset && asset.is_audit_approved;
|
||||
approvedBadge.style.display = (mode === 'view' && isApproved) ? 'inline-flex' : 'none';
|
||||
}
|
||||
|
||||
this.toggleFileUploadUI(mode !== 'view');
|
||||
this.toggleEditOnlyBtns(mode !== 'view');
|
||||
this.updateMapButtonVisibility();
|
||||
|
||||
@@ -681,7 +681,19 @@ export function createListView(container: HTMLElement, config: ListViewConfig) {
|
||||
const rendered = col.render(asset);
|
||||
const rawText = rendered.replace(/<[^>]*>/g, '').trim();
|
||||
const titleAttr = rawText && rawText !== '-' ? `title="${rawText.replace(/"/g, '"')}"` : '';
|
||||
return `<td class="${alignmentClass} ${customClass}" style="${col.width ? `width:${col.width};` : ''}" ${titleAttr}>${rendered}</td>`;
|
||||
|
||||
let displayContent = rendered;
|
||||
if (col.header === ASSET_SCHEMA.LOCATION.ui && asset.is_audit_approved) {
|
||||
const justify = col.align === 'center' ? 'center' : (col.align === 'right' ? 'flex-end' : 'flex-start');
|
||||
displayContent = `
|
||||
<div style="display: inline-flex; align-items: center; gap: 6px; justify-content: ${justify}; width: 100%;">
|
||||
<span>${rendered}</span>
|
||||
<span style="display: inline-flex; align-items: center; background-color: rgba(16, 185, 129, 0.08); color: #059669; border: 1px solid rgba(16, 185, 129, 0.18); padding: 1px 4px; border-radius: 4px; font-size: 10px; font-weight: 600; white-space: nowrap; line-height: 1.2; vertical-align: middle;">승인완료</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
return `<td class="${alignmentClass} ${customClass}" style="${col.width ? `width:${col.width};` : ''}" ${titleAttr}>${displayContent}</td>`;
|
||||
}).join('')}</tr>`).join('');
|
||||
|
||||
tbody.querySelectorAll('.asset-row').forEach((tr, idx) => { tr.addEventListener('click', () => config.onRowClick && config.onRowClick(filtered[idx])); });
|
||||
|
||||
@@ -241,7 +241,10 @@ export async function renderLocationView(container: HTMLElement) {
|
||||
<span class="service-type-badge">${asset.service_type || '운영'}</span>
|
||||
<span class="asset-type-label">${asset.asset_type || 'PC'}</span>
|
||||
</div>
|
||||
<button id="btn-view-from-loc" class="btn btn-primary btn-sm">조회</button>
|
||||
<div style="display: inline-flex; align-items: center; gap: 0.5rem;">
|
||||
${asset.is_audit_approved ? `<span style="display: inline-flex; align-items: center; background-color: rgba(16, 185, 129, 0.08); color: #059669; border: 1px solid rgba(16, 185, 129, 0.18); padding: 2px 6px; border-radius: 4px; font-size: 10px; font-weight: 600; height: 18px; line-height: 1; white-space: nowrap; vertical-align: middle;">승인완료</span>` : ''}
|
||||
<button id="btn-view-from-loc" class="btn btn-primary btn-sm">조회</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user