Merge branch 'thoon' into main
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,3 +7,4 @@ dist/
|
|||||||
Thumbs.db
|
Thumbs.db
|
||||||
backups/
|
backups/
|
||||||
mysql_data/
|
mysql_data/
|
||||||
|
/docs/grafana_integration_proposal.md
|
||||||
|
|||||||
22
mobile.html
22
mobile.html
@@ -294,6 +294,28 @@
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<!-- Custom Confirmation Modal -->
|
||||||
|
<div id="scan-confirm-modal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.75); z-index: 9999; justify-content: center; align-items: center; padding: 1.5rem; backdrop-filter: blur(4px);">
|
||||||
|
<div style="background-color: var(--card); border: 1px solid var(--card-border); border-radius: 16px; padding: 1.5rem; width: 100%; max-width: 320px; display: flex; flex-direction: column; gap: 1.25rem; box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.5), 0 10px 10px -5px rgba(0, 0, 0, 0.4); animation: modalFadeIn 0.25s ease-out;">
|
||||||
|
<h3 style="font-size: 1.1rem; font-weight: 700; color: var(--text); display: flex; align-items: center; gap: 0.5rem;">
|
||||||
|
<span style="display: inline-block; width: 8px; height: 8px; border-radius: 50%; background-color: var(--primary);"></span>
|
||||||
|
실사 등록 확인
|
||||||
|
</h3>
|
||||||
|
<p id="confirm-modal-msg" style="font-size: 0.9rem; color: var(--text-muted); line-height: 1.6; word-break: keep-all;"></p>
|
||||||
|
<div style="display: flex; gap: 0.50rem; margin-top: 0.25rem;">
|
||||||
|
<button id="btn-confirm-cancel" class="btn-action btn-danger" style="flex: 1; padding: 0.75rem; border-radius: 8px; font-weight: 600; font-size: 0.9rem;">취소</button>
|
||||||
|
<button id="btn-confirm-ok" class="btn-action" style="flex: 1; padding: 0.75rem; border-radius: 8px; font-weight: 600; font-size: 0.9rem;">등록</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@keyframes modalFadeIn {
|
||||||
|
from { opacity: 0; transform: scale(0.95); }
|
||||||
|
to { opacity: 1; transform: scale(1); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<script type="module" src="/src/mobile-main.ts"></script>
|
<script type="module" src="/src/mobile-main.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ class DomainAssetModal extends BaseModal {
|
|||||||
|
|
||||||
revertBtn.addEventListener('click', () => {
|
revertBtn.addEventListener('click', () => {
|
||||||
this.setEditLockMode('view');
|
this.setEditLockMode('view');
|
||||||
|
this.isEditMode = false;
|
||||||
if (this.currentAsset) this.fillFormData(this.currentAsset);
|
if (this.currentAsset) this.fillFormData(this.currentAsset);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -272,6 +272,7 @@ class HwAssetModal extends BaseModal {
|
|||||||
|
|
||||||
protected initChildLogic(onSave: () => void, closeModals: () => void): void {
|
protected initChildLogic(onSave: () => void, closeModals: () => void): void {
|
||||||
const saveBtn = document.getElementById('btn-save-hw-asset')!;
|
const saveBtn = document.getElementById('btn-save-hw-asset')!;
|
||||||
|
const revertBtn = document.getElementById('btn-revert-hw-edit')!;
|
||||||
const deleteBtn = document.getElementById('btn-delete-hw-asset')!;
|
const deleteBtn = document.getElementById('btn-delete-hw-asset')!;
|
||||||
const categorySelect = document.getElementById('hw-category') as HTMLSelectElement;
|
const categorySelect = document.getElementById('hw-category') as HTMLSelectElement;
|
||||||
const typeSelect = document.getElementById('hw-asset_type') as HTMLSelectElement;
|
const typeSelect = document.getElementById('hw-asset_type') as HTMLSelectElement;
|
||||||
@@ -412,6 +413,12 @@ class HwAssetModal extends BaseModal {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
revertBtn.addEventListener('click', () => {
|
||||||
|
if (this.currentAsset) {
|
||||||
|
this.open(this.currentAsset, 'view');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
saveBtn.addEventListener('click', async () => {
|
saveBtn.addEventListener('click', async () => {
|
||||||
if (!this.currentAsset) return;
|
if (!this.currentAsset) return;
|
||||||
|
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ class JobSpecModal extends BaseModal {
|
|||||||
|
|
||||||
revertBtn.addEventListener('click', () => {
|
revertBtn.addEventListener('click', () => {
|
||||||
this.setEditLockMode('view');
|
this.setEditLockMode('view');
|
||||||
|
this.isEditMode = false;
|
||||||
if (this.currentAsset) this.fillFormData(this.currentAsset);
|
if (this.currentAsset) this.fillFormData(this.currentAsset);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ class PartsMasterModal extends BaseModal {
|
|||||||
|
|
||||||
revertBtn.addEventListener('click', () => {
|
revertBtn.addEventListener('click', () => {
|
||||||
this.setEditLockMode('view');
|
this.setEditLockMode('view');
|
||||||
|
this.isEditMode = false;
|
||||||
if (this.currentAsset) this.fillFormData(this.currentAsset);
|
if (this.currentAsset) this.fillFormData(this.currentAsset);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -278,6 +278,7 @@ class SwAssetModal extends BaseModal {
|
|||||||
|
|
||||||
revertBtn.addEventListener('click', () => {
|
revertBtn.addEventListener('click', () => {
|
||||||
this.setEditLockMode('view');
|
this.setEditLockMode('view');
|
||||||
|
this.isEditMode = false;
|
||||||
if (this.currentAsset) this.fillFormData(this.currentAsset);
|
if (this.currentAsset) this.fillFormData(this.currentAsset);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ class UserModal extends BaseModal {
|
|||||||
|
|
||||||
revertBtn.addEventListener('click', () => {
|
revertBtn.addEventListener('click', () => {
|
||||||
this.setEditLockMode('view');
|
this.setEditLockMode('view');
|
||||||
|
this.isEditMode = false;
|
||||||
if (this.currentAsset) this.fillFormData(this.currentAsset);
|
if (this.currentAsset) this.fillFormData(this.currentAsset);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { state } from '../core/state';
|
|||||||
const MENU_CONFIG: any = {
|
const MENU_CONFIG: any = {
|
||||||
hw: {
|
hw: {
|
||||||
label: '하드웨어',
|
label: '하드웨어',
|
||||||
tabs: ['대시보드', '서버', 'PC', '스토리지', '공간정보장비', 'PC부품', '부품 마스터', '네트워크', '업무지원장비']
|
tabs: ['대시보드', '서버', 'PC', '스토리지', '공간정보장비', 'PC부품', '네트워크', '업무지원장비']
|
||||||
},
|
},
|
||||||
sw: {
|
sw: {
|
||||||
label: '소프트웨어',
|
label: '소프트웨어',
|
||||||
@@ -65,18 +65,17 @@ export function renderNavigation(onTabChange: (tab: string) => void) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (state.currentUserRole === 'admin' && catKey === 'hw') {
|
if (state.currentUserRole === 'admin' && catKey === 'hw') {
|
||||||
visibleTabs = ['대시보드', '관리도구', '실사 승인', '위치지정'];
|
visibleTabs = ['대시보드', '관리도구', '실사 승인', '위치지정', '부품 마스터'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (visibleTabs.length === 0) return;
|
if (visibleTabs.length === 0) return;
|
||||||
|
|
||||||
visibleTabs.forEach((tab: string) => {
|
visibleTabs.forEach((tab: string) => {
|
||||||
if (tab === '부품 마스터') return;
|
|
||||||
const item = document.createElement('div');
|
const item = document.createElement('div');
|
||||||
const isActive = state.activeSubTab === tab;
|
const isActive = state.activeSubTab === tab;
|
||||||
item.className = `gnb-trigger ${isActive ? 'active' : ''}`;
|
item.className = `gnb-trigger ${isActive ? 'active' : ''}`;
|
||||||
|
|
||||||
const isSubMenu = tab === '실사 승인' || tab === '위치지정';
|
const isSubMenu = tab === '실사 승인' || tab === '위치지정' || tab === '부품 마스터';
|
||||||
if (isSubMenu) {
|
if (isSubMenu) {
|
||||||
item.innerHTML = `<span style="opacity: 0.5; margin-right: 3px; font-family: sans-serif;">↳</span>${tab}`;
|
item.innerHTML = `<span style="opacity: 0.5; margin-right: 3px; font-family: sans-serif;">↳</span>${tab}`;
|
||||||
item.style.fontSize = '11px';
|
item.style.fontSize = '11px';
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ export interface FilterOptions {
|
|||||||
showField?: boolean;
|
showField?: boolean;
|
||||||
showType?: boolean;
|
showType?: boolean;
|
||||||
showStatus?: boolean;
|
showStatus?: boolean;
|
||||||
|
showPartCategory?: boolean;
|
||||||
|
showPartTier?: boolean;
|
||||||
extraHTML?: string;
|
extraHTML?: string;
|
||||||
onFilterChange: (filters: any) => void;
|
onFilterChange: (filters: any) => void;
|
||||||
initialFilters?: any;
|
initialFilters?: any;
|
||||||
@@ -37,9 +39,11 @@ export function renderFilterBar(container: HTMLElement, options: FilterOptions)
|
|||||||
showField = false,
|
showField = false,
|
||||||
showType = false,
|
showType = false,
|
||||||
showStatus = false,
|
showStatus = false,
|
||||||
|
showPartCategory = false,
|
||||||
|
showPartTier = false,
|
||||||
extraHTML = '',
|
extraHTML = '',
|
||||||
onFilterChange,
|
onFilterChange,
|
||||||
initialFilters = { keyword: '', corp: '', dept: '', loc: '', field: '', type: '', status: '' },
|
initialFilters = { keyword: '', corp: '', dept: '', loc: '', field: '', type: '', status: '', partCategory: '', partTier: '' },
|
||||||
fullList = []
|
fullList = []
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
@@ -104,6 +108,22 @@ export function renderFilterBar(container: HTMLElement, options: FilterOptions)
|
|||||||
${getUnique('CURRENT_DEPT').map(v => `<option value="${v}" ${initialFilters.dept === v ? 'selected' : ''}>${v}</option>`).join('')}
|
${getUnique('CURRENT_DEPT').map(v => `<option value="${v}" ${initialFilters.dept === v ? 'selected' : ''}>${v}</option>`).join('')}
|
||||||
</select>
|
</select>
|
||||||
</div>` : ''}
|
</div>` : ''}
|
||||||
|
${showPartCategory ? `
|
||||||
|
<div class="search-item">
|
||||||
|
<label>분류</label>
|
||||||
|
<select id="filter-part-category">
|
||||||
|
<option value="">전체 분류</option>
|
||||||
|
${getUnique('category').map(v => `<option value="${v}" ${initialFilters.partCategory === v ? 'selected' : ''}>${v}</option>`).join('')}
|
||||||
|
</select>
|
||||||
|
</div>` : ''}
|
||||||
|
${showPartTier ? `
|
||||||
|
<div class="search-item">
|
||||||
|
<label>성능등급</label>
|
||||||
|
<select id="filter-part-tier">
|
||||||
|
<option value="">전체 등급</option>
|
||||||
|
${getUnique('score_tier').map(v => `<option value="${v}" ${initialFilters.partTier === v ? 'selected' : ''}>${v}</option>`).join('')}
|
||||||
|
</select>
|
||||||
|
</div>` : ''}
|
||||||
${extraHTML}
|
${extraHTML}
|
||||||
<button id="btn-reset-filters" class="btn btn-outline btn-reset">
|
<button id="btn-reset-filters" class="btn btn-outline btn-reset">
|
||||||
<i data-lucide="refresh-ccw" class="icon-sm"></i> ${UI_TEXT.ACTION.RESET_FILTER}
|
<i data-lucide="refresh-ccw" class="icon-sm"></i> ${UI_TEXT.ACTION.RESET_FILTER}
|
||||||
@@ -126,7 +146,9 @@ export function renderFilterBar(container: HTMLElement, options: FilterOptions)
|
|||||||
loc: (container.querySelector('#filter-loc') as HTMLSelectElement)?.value || '',
|
loc: (container.querySelector('#filter-loc') as HTMLSelectElement)?.value || '',
|
||||||
field: (container.querySelector('#filter-field') as HTMLSelectElement)?.value || '',
|
field: (container.querySelector('#filter-field') as HTMLSelectElement)?.value || '',
|
||||||
type: (container.querySelector('#filter-type') as HTMLSelectElement)?.value || '',
|
type: (container.querySelector('#filter-type') as HTMLSelectElement)?.value || '',
|
||||||
status: (container.querySelector('#filter-status') as HTMLSelectElement)?.value || ''
|
status: (container.querySelector('#filter-status') as HTMLSelectElement)?.value || '',
|
||||||
|
partCategory: (container.querySelector('#filter-part-category') as HTMLSelectElement)?.value || '',
|
||||||
|
partTier: (container.querySelector('#filter-part-tier') as HTMLSelectElement)?.value || ''
|
||||||
};
|
};
|
||||||
onFilterChange(filters);
|
onFilterChange(filters);
|
||||||
};
|
};
|
||||||
@@ -138,9 +160,11 @@ export function renderFilterBar(container: HTMLElement, options: FilterOptions)
|
|||||||
container.querySelector('#filter-field')?.addEventListener('change', triggerChange);
|
container.querySelector('#filter-field')?.addEventListener('change', triggerChange);
|
||||||
container.querySelector('#filter-type')?.addEventListener('change', triggerChange);
|
container.querySelector('#filter-type')?.addEventListener('change', triggerChange);
|
||||||
container.querySelector('#filter-status')?.addEventListener('change', triggerChange);
|
container.querySelector('#filter-status')?.addEventListener('change', triggerChange);
|
||||||
|
container.querySelector('#filter-part-category')?.addEventListener('change', triggerChange);
|
||||||
|
container.querySelector('#filter-part-tier')?.addEventListener('change', triggerChange);
|
||||||
|
|
||||||
container.querySelector('#btn-reset-filters')?.addEventListener('click', () => {
|
container.querySelector('#btn-reset-filters')?.addEventListener('click', () => {
|
||||||
['filter-keyword', 'filter-corp', 'filter-dept', 'filter-loc', 'filter-field', 'filter-type', 'filter-status'].forEach(id => {
|
['filter-keyword', 'filter-corp', 'filter-dept', 'filter-loc', 'filter-field', 'filter-type', 'filter-status', 'filter-part-category', 'filter-part-tier'].forEach(id => {
|
||||||
const el = container.querySelector(`#${id}`);
|
const el = container.querySelector(`#${id}`);
|
||||||
if (el) (el as any).value = '';
|
if (el) (el as any).value = '';
|
||||||
});
|
});
|
||||||
@@ -153,16 +177,20 @@ export function renderFilterBar(container: HTMLElement, options: FilterOptions)
|
|||||||
*/
|
*/
|
||||||
export function applyCommonFilters(list: any[], filters: any, searchKeys: (keyof typeof ASSET_SCHEMA)[]) {
|
export function applyCommonFilters(list: any[], filters: any, searchKeys: (keyof typeof ASSET_SCHEMA)[]) {
|
||||||
return list.filter(item => {
|
return list.filter(item => {
|
||||||
const matchKeyword = !filters.keyword || searchKeys.some(key =>
|
const matchKeyword = !filters.keyword || searchKeys.some(key => {
|
||||||
String(item[ASSET_SCHEMA[key].key] || item[ASSET_SCHEMA[key].db] || '').toLowerCase().includes(filters.keyword)
|
const schema = ASSET_SCHEMA[key];
|
||||||
);
|
const val = schema ? (item[schema.key] || item[schema.db]) : item[key];
|
||||||
|
return String(val || '').toLowerCase().includes(filters.keyword);
|
||||||
|
});
|
||||||
const matchCorp = !filters.corp || (item[ASSET_SCHEMA.PURCHASE_CORP.key] || item[ASSET_SCHEMA.PURCHASE_CORP.db]) === filters.corp;
|
const matchCorp = !filters.corp || (item[ASSET_SCHEMA.PURCHASE_CORP.key] || item[ASSET_SCHEMA.PURCHASE_CORP.db]) === filters.corp;
|
||||||
const matchDept = !filters.dept || (item[ASSET_SCHEMA.CURRENT_DEPT.key] || item[ASSET_SCHEMA.CURRENT_DEPT.db]) === filters.dept;
|
const matchDept = !filters.dept || (item[ASSET_SCHEMA.CURRENT_DEPT.key] || item[ASSET_SCHEMA.CURRENT_DEPT.db]) === filters.dept;
|
||||||
const matchLoc = !filters.loc || (item[ASSET_SCHEMA.LOCATION.key] || item[ASSET_SCHEMA.LOCATION.db]) === filters.loc;
|
const matchLoc = !filters.loc || (item[ASSET_SCHEMA.LOCATION.key] || item[ASSET_SCHEMA.LOCATION.db]) === filters.loc;
|
||||||
const matchField = !filters.field || (item[ASSET_SCHEMA.SW_FIELD.key] || item[ASSET_SCHEMA.SW_FIELD.db]) === filters.field;
|
const matchField = !filters.field || (item[ASSET_SCHEMA.SW_FIELD.key] || item[ASSET_SCHEMA.SW_FIELD.db]) === filters.field;
|
||||||
const matchType = !filters.type || (item[ASSET_SCHEMA.ASSET_TYPE.key] || item[ASSET_SCHEMA.ASSET_TYPE.db]) === filters.type;
|
const matchType = !filters.type || (item[ASSET_SCHEMA.ASSET_TYPE.key] || item[ASSET_SCHEMA.ASSET_TYPE.db]) === filters.type;
|
||||||
const matchStatus = !filters.status || (item[ASSET_SCHEMA.HW_STATUS.key] || item[ASSET_SCHEMA.HW_STATUS.db]) === filters.status;
|
const matchStatus = !filters.status || (item[ASSET_SCHEMA.HW_STATUS.key] || item[ASSET_SCHEMA.HW_STATUS.db]) === filters.status;
|
||||||
|
const matchPartCategory = !filters.partCategory || item.category === filters.partCategory;
|
||||||
|
const matchPartTier = !filters.partTier || item.score_tier === filters.partTier;
|
||||||
|
|
||||||
return matchKeyword && matchCorp && matchDept && matchLoc && matchField && matchType && matchStatus;
|
return matchKeyword && matchCorp && matchDept && matchLoc && matchField && matchType && matchStatus && matchPartCategory && matchPartTier;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,15 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const manualInput = document.getElementById('manual-code-input') as HTMLInputElement;
|
const manualInput = document.getElementById('manual-code-input') as HTMLInputElement;
|
||||||
const manualSubmitBtn = document.getElementById('btn-submit-manual') as HTMLButtonElement;
|
const manualSubmitBtn = document.getElementById('btn-submit-manual') as HTMLButtonElement;
|
||||||
|
|
||||||
|
// 확인 모달 관련 셀렉터 및 상태 변수
|
||||||
|
const confirmModal = document.getElementById('scan-confirm-modal')!;
|
||||||
|
const confirmModalMsg = document.getElementById('confirm-modal-msg')!;
|
||||||
|
const btnConfirmCancel = document.getElementById('btn-confirm-cancel') as HTMLButtonElement;
|
||||||
|
const btnConfirmOk = document.getElementById('btn-confirm-ok') as HTMLButtonElement;
|
||||||
|
|
||||||
let html5QrcodeScanner: any = null;
|
let html5QrcodeScanner: any = null;
|
||||||
|
let isModalOpen = false;
|
||||||
|
let pendingAssetCode = '';
|
||||||
|
|
||||||
// Initialize UI based on current session lock
|
// Initialize UI based on current session lock
|
||||||
updateLocationUI();
|
updateLocationUI();
|
||||||
@@ -42,6 +50,33 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
manualInput.value = '';
|
manualInput.value = '';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 확인 모달 버튼 이벤트 바인딩
|
||||||
|
btnConfirmCancel.addEventListener('click', () => {
|
||||||
|
closeConfirmModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
btnConfirmOk.addEventListener('click', () => {
|
||||||
|
const lockedLoc = sessionStorage.getItem(SESSION_LOC_KEY);
|
||||||
|
if (pendingAssetCode && lockedLoc) {
|
||||||
|
submitAssetAudit(pendingAssetCode, lockedLoc);
|
||||||
|
}
|
||||||
|
closeConfirmModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
function openConfirmModal(assetCode: string, locationCode: string) {
|
||||||
|
isModalOpen = true;
|
||||||
|
pendingAssetCode = assetCode;
|
||||||
|
confirmModalMsg.innerHTML = `자산 <strong>[${assetCode}]</strong>을<br>현재 위치 <strong>[${locationCode}]</strong>에 실사 등록하시겠습니까?`;
|
||||||
|
confirmModal.style.display = 'flex';
|
||||||
|
vibrateDevice(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeConfirmModal() {
|
||||||
|
confirmModal.style.display = 'none';
|
||||||
|
isModalOpen = false;
|
||||||
|
pendingAssetCode = '';
|
||||||
|
}
|
||||||
|
|
||||||
// --- Core Scanner Functions ---
|
// --- Core Scanner Functions ---
|
||||||
|
|
||||||
function initScanner() {
|
function initScanner() {
|
||||||
@@ -80,6 +115,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function processScannedCode(rawCode: string) {
|
function processScannedCode(rawCode: string) {
|
||||||
|
if (isModalOpen) return; // 모달이 이미 열려 있는 경우 추가 스캔 차단
|
||||||
|
|
||||||
// QR 코드 인쇄 폼 등으로 인한 개행 문자(\r, \n) 및 모든 공백 문자(\s)를 제거
|
// QR 코드 인쇄 폼 등으로 인한 개행 문자(\r, \n) 및 모든 공백 문자(\s)를 제거
|
||||||
let code = rawCode.replace(/[\r\n]/g, '').replace(/\s+/g, '').trim();
|
let code = rawCode.replace(/[\r\n]/g, '').replace(/\s+/g, '').trim();
|
||||||
|
|
||||||
@@ -114,8 +151,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Submit matching info to server
|
// 바로 전송하는 대신 확인 모달 팝업을 띄움
|
||||||
submitAssetAudit(code, lockedLoc);
|
openConfirmModal(code, lockedLoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitAssetAudit(assetCode: string, locationCode: string) {
|
async function submitAssetAudit(assetCode: string, locationCode: string) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { setupTableSorting, SortState } from '../../core/tableHandler';
|
|||||||
import { renderFilterBar, applyCommonFilters } from '../../core/filterHandler';
|
import { renderFilterBar, applyCommonFilters } from '../../core/filterHandler';
|
||||||
import { state } from '../../core/state';
|
import { state } from '../../core/state';
|
||||||
import { IMAGE_LOCATIONS } from '../../components/Modal/SharedData';
|
import { IMAGE_LOCATIONS } from '../../components/Modal/SharedData';
|
||||||
|
import { createIcons, Plus, Settings, RefreshCcw } from 'lucide';
|
||||||
import './table.css';
|
import './table.css';
|
||||||
|
|
||||||
declare var Chart: any;
|
declare var Chart: any;
|
||||||
@@ -31,6 +32,8 @@ export interface ListViewConfig {
|
|||||||
showType?: boolean;
|
showType?: boolean;
|
||||||
showStatus?: boolean;
|
showStatus?: boolean;
|
||||||
showPosition?: boolean;
|
showPosition?: boolean;
|
||||||
|
showPartCategory?: boolean;
|
||||||
|
showPartTier?: boolean;
|
||||||
};
|
};
|
||||||
columns: ColumnDef[];
|
columns: ColumnDef[];
|
||||||
onRowClick?: (asset: any) => void;
|
onRowClick?: (asset: any) => void;
|
||||||
@@ -50,7 +53,10 @@ export function createListView(container: HTMLElement, config: ListViewConfig) {
|
|||||||
}
|
}
|
||||||
const filterKey = config.title;
|
const filterKey = config.title;
|
||||||
if (!(state as any).listFilters[filterKey]) {
|
if (!(state as any).listFilters[filterKey]) {
|
||||||
(state as any).listFilters[filterKey] = { keyword: '', corp: '', dept: '', loc: '', field: '', type: '', status: '' };
|
(state as any).listFilters[filterKey] = {
|
||||||
|
keyword: '', corp: '', dept: '', loc: '', field: '', type: '', status: '',
|
||||||
|
partCategory: '', partTier: ''
|
||||||
|
};
|
||||||
}
|
}
|
||||||
let currentFilters: any = (state as any).listFilters[filterKey];
|
let currentFilters: any = (state as any).listFilters[filterKey];
|
||||||
|
|
||||||
@@ -849,5 +855,9 @@ export function createListView(container: HTMLElement, config: ListViewConfig) {
|
|||||||
chkBox?.addEventListener('change', handleToggle);
|
chkBox?.addEventListener('change', handleToggle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createIcons({
|
||||||
|
icons: { Plus, Settings, RefreshCcw }
|
||||||
|
});
|
||||||
|
|
||||||
switchView();
|
switchView();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ export function renderPartsMasterList(container: HTMLElement) {
|
|||||||
keywordLabel: '부품명 / 등급 검색',
|
keywordLabel: '부품명 / 등급 검색',
|
||||||
showLoc: false,
|
showLoc: false,
|
||||||
showDept: false,
|
showDept: false,
|
||||||
showType: false
|
showType: false,
|
||||||
|
showPartCategory: true,
|
||||||
|
showPartTier: true
|
||||||
},
|
},
|
||||||
onRowClick: (component) => openPartsMasterModal(component, 'view'),
|
onRowClick: (component) => openPartsMasterModal(component, 'view'),
|
||||||
columns: [
|
columns: [
|
||||||
@@ -72,6 +74,7 @@ export function renderPartsMasterList(container: HTMLElement) {
|
|||||||
title: '직무별 기준 사양',
|
title: '직무별 기준 사양',
|
||||||
dataSource: () => state.masterData.jobSpecs || [],
|
dataSource: () => state.masterData.jobSpecs || [],
|
||||||
searchKeys: ['job_name', 'cpu_standard', 'ram_standard', 'gpu_standard', 'remarks'],
|
searchKeys: ['job_name', 'cpu_standard', 'ram_standard', 'gpu_standard', 'remarks'],
|
||||||
|
persistentSortState: { key: 'id', direction: 'asc' },
|
||||||
filterOptions: {
|
filterOptions: {
|
||||||
keywordLabel: '직무명 / 사양 검색',
|
keywordLabel: '직무명 / 사양 검색',
|
||||||
showLoc: false,
|
showLoc: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user