자산 상세 모달 수정 취소 오류 수정, 모바일 QR 스캔 전송 확인 모달 도입, 부품 마스터 필터/정렬 개선, 부품 마스터 GNB 관리자 하위 메뉴로 이동 및 그라파나 검토 문서 무시 설정

This commit is contained in:
이태훈
2026-06-29 16:50:04 +09:00
parent 10e27c0096
commit 615523fb90
13 changed files with 127 additions and 15 deletions

View File

@@ -15,6 +15,8 @@ export interface FilterOptions {
showField?: boolean;
showType?: boolean;
showStatus?: boolean;
showPartCategory?: boolean;
showPartTier?: boolean;
extraHTML?: string;
onFilterChange: (filters: any) => void;
initialFilters?: any;
@@ -37,9 +39,11 @@ export function renderFilterBar(container: HTMLElement, options: FilterOptions)
showField = false,
showType = false,
showStatus = false,
showPartCategory = false,
showPartTier = false,
extraHTML = '',
onFilterChange,
initialFilters = { keyword: '', corp: '', dept: '', loc: '', field: '', type: '', status: '' },
initialFilters = { keyword: '', corp: '', dept: '', loc: '', field: '', type: '', status: '', partCategory: '', partTier: '' },
fullList = []
} = 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('')}
</select>
</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}
<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}
@@ -126,7 +146,9 @@ export function renderFilterBar(container: HTMLElement, options: FilterOptions)
loc: (container.querySelector('#filter-loc') as HTMLSelectElement)?.value || '',
field: (container.querySelector('#filter-field') 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);
};
@@ -138,9 +160,11 @@ export function renderFilterBar(container: HTMLElement, options: FilterOptions)
container.querySelector('#filter-field')?.addEventListener('change', triggerChange);
container.querySelector('#filter-type')?.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', () => {
['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}`);
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)[]) {
return list.filter(item => {
const matchKeyword = !filters.keyword || searchKeys.some(key =>
String(item[ASSET_SCHEMA[key].key] || item[ASSET_SCHEMA[key].db] || '').toLowerCase().includes(filters.keyword)
);
const matchKeyword = !filters.keyword || searchKeys.some(key => {
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 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 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 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;
});
}