diff --git a/src/Web/wwwroot/js/pid.js b/src/Web/wwwroot/js/pid.js index 0277e0d..1064b66 100644 --- a/src/Web/wwwroot/js/pid.js +++ b/src/Web/wwwroot/js/pid.js @@ -354,14 +354,22 @@ function pidTogglePrefixPanel() { } const CATEGORY_META = { - instrument: { label: 'Instrument', badge: 'ok' }, - power_equipment: { label: 'Power Equipment', badge: 'warn' }, - storage_equipment: { label: 'Storage Equipment', badge: 'inf' }, - process_equipment: { label: 'Process Equipment', badge: '' }, - utility_equipment: { label: 'Utility Equipment', badge: 'warn' }, - pipings: { label: 'Pipings', badge: 'ok' } + // ── instrument 가상 분할 (DB category='instrument', tag_dcs로 구분) ── + instrument_dcs: { label: 'DCS 태그', badge: 'warn', dbCat: 'instrument', tagDcs: true }, + instrument_field: { label: '현장 계기', badge: 'ok', dbCat: 'instrument', tagDcs: false }, + // ── 그 외 equipment / pipings ── + power_equipment: { label: 'Power Equipment', badge: 'warn', dbCat: 'power_equipment', tagDcs: false }, + storage_equipment:{ label: 'Storage Equipment', badge: 'inf', dbCat: 'storage_equipment', tagDcs: false }, + process_equipment:{ label: 'Process Equipment', badge: '', dbCat: 'process_equipment', tagDcs: false }, + utility_equipment:{ label: 'Utility Equipment', badge: 'warn', dbCat: 'utility_equipment', tagDcs: false }, + pipings: { label: 'Pipings', badge: 'ok', dbCat: 'pipings', tagDcs: false }, + // ── 장비 테이블 배지용 (equipment 목록의 r.category 값과 매핑) ── + instrument: { label: 'Instrument', badge: 'ok' }, }; -const CATEGORY_ORDER = ['instrument', 'power_equipment', 'storage_equipment', 'process_equipment', 'utility_equipment', 'pipings']; +const CATEGORY_ORDER = [ + 'instrument_dcs', 'instrument_field', + 'power_equipment', 'storage_equipment', 'process_equipment', 'utility_equipment', 'pipings' +]; function pidCategoryBadge(cat) { return CATEGORY_META[cat] ? CATEGORY_META[cat].badge : ''; @@ -383,50 +391,60 @@ async function pidRefreshPrefixRules() { return; } + // instrument → tag_dcs 기준으로 가상 분할 const grouped = {}; for (const r of items) { - if (!grouped[r.category]) grouped[r.category] = []; - grouped[r.category].push(r); + let key = r.category; + if (r.category === 'instrument') { + key = r.tagDcs ? 'instrument_dcs' : 'instrument_field'; + } + if (!grouped[key]) grouped[key] = []; + grouped[key].push(r); } let html = ''; - for (const cat of CATEGORY_ORDER) { - const rules = grouped[cat]; - if (!rules) continue; - const meta = CATEGORY_META[cat] || { label: cat, badge: '' }; - rules.sort((a, b) => a.sortOrder - b.sortOrder); + for (const vcat of CATEGORY_ORDER) { + const rules = grouped[vcat]; + const meta = CATEGORY_META[vcat] || { label: vcat, badge: '', dbCat: vcat, tagDcs: false }; + rules?.sort((a, b) => a.sortOrder - b.sortOrder); - html += `
+ // 그룹 헤더: 규칙이 0건이어도 추가 입력행은 항상 표시 + const count = rules ? rules.length : 0; + const isInstr = vcat === 'instrument_dcs' || vcat === 'instrument_field'; + + html += `
${meta.label} - ${rules.length} + ${count} - + - - +
`; - for (const r of rules) { - html += `
- - - - - - - - -
`; + if (rules) { + for (const r of rules) { + // instrument 그룹은 DCS 이동용 토글 표시 (체크 해제 시 반대 그룹으로 이동) + const dcsToggle = isInstr + ? `` + : ''; + html += `
+ + + + ${dcsToggle} + + + + +
`; + } } html += `
`; @@ -438,12 +456,21 @@ async function pidRefreshPrefixRules() { } } -async function pidAddPrefixRule(category) { +// 가상 카테고리 키 → { DB category, tagDcs } 변환 +function pidResolveCat(vcat) { + const meta = CATEGORY_META[vcat]; + return { + category: meta?.dbCat ?? vcat, + tagDcs: meta?.tagDcs ?? false + }; +} + +async function pidAddPrefixRule(vcat) { const row = event.target.closest('.pid-cat-add'); const prefix = row.querySelector('.pid-cat-prefix-input').value.trim(); - const desc = row.querySelector('.pid-cat-desc-input').value.trim(); - const order = parseInt(row.querySelector('.pid-cat-order-input').value) || 10; - const tagDcs = row.querySelector('.pid-cat-dcs-input')?.checked ?? false; + const desc = row.querySelector('.pid-cat-desc-input').value.trim(); + const order = parseInt(row.querySelector('.pid-cat-order-input').value) || 10; + const { category, tagDcs } = pidResolveCat(vcat); // 그룹이 tagDcs 결정 if (!prefix) { alert('Prefix를 입력하세요.'); return; } @@ -466,14 +493,17 @@ async function pidAddPrefixRule(category) { } async function pidUpdatePrefixRule(id, btn) { - const row = btn.closest('.pid-cat-row'); + const row = btn.closest('.pid-cat-row'); const group = btn.closest('.pid-cat-group'); - const catIdx = [...group.parentElement.children].indexOf(group); - const cat = CATEGORY_ORDER[catIdx]; + const vcat = group.dataset.vcat; // data-vcat 속성으로 가상 키 읽기 + const { category } = pidResolveCat(vcat); const prefix = row.querySelector('.pid-cat-prefix-input').value.trim(); - const desc = row.querySelector('.pid-cat-desc-input').value.trim(); - const order = parseInt(row.querySelector('.pid-cat-order-input').value) || 10; - const tagDcs = row.querySelector('.pid-cat-dcs-input')?.checked ?? false; + const desc = row.querySelector('.pid-cat-desc-input').value.trim(); + const order = parseInt(row.querySelector('.pid-cat-order-input').value) || 10; + // instrument 행에는 DCS 토글이 있음 → 체크 상태로 tagDcs 결정 (그룹 이동 가능) + // 그 외 행에는 토글 없음 → 그룹 기본값 사용 + const dcsInput = row.querySelector('.pid-cat-dcs-input'); + const tagDcs = dcsInput ? dcsInput.checked : (CATEGORY_META[vcat]?.tagDcs ?? false); if (!prefix) { alert('Prefix를 입력하세요.'); return; } @@ -481,7 +511,7 @@ async function pidUpdatePrefixRule(id, btn) { const res = await fetch(`/api/pid/prefix-rules/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ prefix, category: cat, tagDcs, description: desc, sortOrder: order }) + body: JSON.stringify({ prefix, category, tagDcs, description: desc, sortOrder: order }) }); if (!res.ok) { const err = await res.json().catch(() => ({ error: res.statusText }));