update
This commit is contained in:
+392
-73
@@ -15,8 +15,14 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const bar = document.getElementById('progressBar');
|
||||
if (!bar) return;
|
||||
const v = Math.max(0, Math.min(100, Number(val) || 0));
|
||||
|
||||
// 부모 컨테이너가 숨겨져 있다면 표시
|
||||
|
||||
// 부모 컨테이너(섹션)가 숨겨져 있다면 표시
|
||||
const section = document.getElementById('progressSection');
|
||||
if (section && section.style.display === 'none') {
|
||||
section.style.display = 'block';
|
||||
}
|
||||
|
||||
// (Legacy support) 부모 컨테이너가 숨겨져 있다면 표시
|
||||
const progressContainer = bar.closest('.progress');
|
||||
if (progressContainer && progressContainer.parentElement.classList.contains('d-none')) {
|
||||
progressContainer.parentElement.classList.remove('d-none');
|
||||
@@ -25,8 +31,87 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
bar.style.width = v + '%';
|
||||
bar.setAttribute('aria-valuenow', v);
|
||||
bar.innerHTML = `<span class="fw-semibold small">${v}%</span>`;
|
||||
|
||||
// 100% 도달 시 애니메이션 효과 제어 등은 필요 시 추가
|
||||
};
|
||||
|
||||
/**
|
||||
* 테이블 정렬 함수
|
||||
* @param {string} tableId - 테이블 ID
|
||||
* @param {number} colIdx - 정렬할 컬럼 인덱스 (0부터 시작)
|
||||
*/
|
||||
window.sortTable = function (tableId, colIdx) {
|
||||
const table = document.getElementById(tableId);
|
||||
if (!table) return;
|
||||
const tbody = table.querySelector('tbody');
|
||||
const rows = Array.from(tbody.querySelectorAll('tr'));
|
||||
|
||||
// 데이터가 없거나 1개 뿐이면 패스
|
||||
if (rows.length <= 1) return;
|
||||
if (rows[0].querySelector('td[colspan]')) return; // "파일이 없습니다" 행
|
||||
|
||||
const th = table.querySelectorAll('th')[colIdx];
|
||||
let asc = !th.classList.contains('sorted-asc'); // 현재 상태의 반대로 토글
|
||||
|
||||
// 모든 헤더 초기화
|
||||
table.querySelectorAll('th').forEach(h => {
|
||||
h.classList.remove('sorted-asc', 'sorted-desc');
|
||||
const icon = h.querySelector('i');
|
||||
if (icon) icon.className = 'bi bi-arrow-down-up small text-muted';
|
||||
});
|
||||
|
||||
// 현재 헤더 상태 업데이트
|
||||
th.classList.add(asc ? 'sorted-asc' : 'sorted-desc');
|
||||
const icon = th.querySelector('i');
|
||||
if (icon) icon.className = asc ? 'bi bi-sort-down-alt text-primary' : 'bi bi-sort-up-alt text-primary';
|
||||
|
||||
// 정렬
|
||||
rows.sort((rowA, rowB) => {
|
||||
const cellA = rowA.children[colIdx];
|
||||
const cellB = rowB.children[colIdx];
|
||||
|
||||
// data-sort 속성이 있으면 그것을 우선 사용 (날짜/숫자 등)
|
||||
const valA = cellA.dataset.sort || cellA.textContent.trim();
|
||||
const valB = cellB.dataset.sort || cellB.textContent.trim();
|
||||
|
||||
const isNum = !isNaN(parseFloat(valA)) && isFinite(valA) && !isNaN(parseFloat(valB)) && isFinite(valB);
|
||||
|
||||
if (isNum) {
|
||||
return asc ? (parseFloat(valA) - parseFloat(valB)) : (parseFloat(valB) - parseFloat(valA));
|
||||
} else {
|
||||
return asc ? valA.localeCompare(valB) : valB.localeCompare(valA);
|
||||
}
|
||||
});
|
||||
|
||||
// 다시 추가 (재정렬)
|
||||
rows.forEach(row => tbody.appendChild(row));
|
||||
};
|
||||
|
||||
/**
|
||||
* 테이블 필터링 함수
|
||||
* @param {string} tableId - 테이블 ID
|
||||
* @param {string} text - 검색어
|
||||
*/
|
||||
window.filterTable = function (tableId, text) {
|
||||
const table = document.getElementById(tableId);
|
||||
if (!table) return;
|
||||
const tbody = table.querySelector('tbody');
|
||||
const rows = tbody.querySelectorAll('tr');
|
||||
const query = text.toLowerCase().trim();
|
||||
|
||||
let visibleCount = 0;
|
||||
rows.forEach(row => {
|
||||
if (row.querySelector('td[colspan]')) return; // 메시지 행 제외
|
||||
|
||||
// 첫 번째 셀(파일명)만 검색 대상이라고 가정하거나, 전체 텍스트 검색
|
||||
const rowText = row.textContent.toLowerCase();
|
||||
if (rowText.includes(query)) {
|
||||
row.style.display = '';
|
||||
visibleCount++;
|
||||
} else {
|
||||
row.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// 검색 결과 없음 처리 (Optional)
|
||||
};
|
||||
|
||||
// 줄 수 카운터 (script.js에서 가져옴)
|
||||
@@ -88,7 +173,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
// TomSelect 적용 전/후 모두 대응하기 위해 이벤트 리스너 등록
|
||||
toggleXml();
|
||||
scriptSelect.addEventListener('change', toggleXml);
|
||||
|
||||
|
||||
// TomSelect 초기화
|
||||
new TomSelect("#script", {
|
||||
create: false,
|
||||
@@ -135,9 +220,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
// 진행바 표시 및 초기화
|
||||
const progressContainer = progressBar.closest('.progress');
|
||||
if (progressContainer && progressContainer.parentElement) {
|
||||
// 이미 존재하는 row 등을 찾아서 보여주기 (필요시)
|
||||
// 이미 존재하는 row 등을 찾아서 보여주기 (필요시)
|
||||
}
|
||||
|
||||
|
||||
window.updateProgress(100); // 스캔 중임을 알리기 위해 꽉 채움 (애니메이션)
|
||||
progressBar.classList.add('progress-bar-striped', 'progress-bar-animated');
|
||||
progressBar.textContent = 'IP 스캔 중...';
|
||||
@@ -185,6 +270,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (progressBar) {
|
||||
window.updateProgress(0);
|
||||
progressBar.classList.remove('progress-bar-striped', 'progress-bar-animated');
|
||||
|
||||
const section = document.getElementById('progressSection');
|
||||
if (section) section.style.display = 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -194,11 +282,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const btnClear = document.getElementById('btnClearIps');
|
||||
if (btnClear) {
|
||||
btnClear.addEventListener('click', () => {
|
||||
const ipsTextarea = document.getElementById('ips');
|
||||
if (ipsTextarea && confirm('입력된 IP 목록을 모두 지우시겠습니까?')) {
|
||||
ipsTextarea.value = '';
|
||||
ipsTextarea.dispatchEvent(new Event('input'));
|
||||
}
|
||||
const ipsTextarea = document.getElementById('ips');
|
||||
if (ipsTextarea && confirm('입력된 IP 목록을 모두 지우시겠습니까?')) {
|
||||
ipsTextarea.value = '';
|
||||
ipsTextarea.dispatchEvent(new Event('input'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -209,6 +297,15 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
ev.preventDefault();
|
||||
|
||||
const formData = new FormData(ipForm);
|
||||
|
||||
// [NEW] GUID 작업 시 로컬 스토리지의 슬롯 우선순위 추가
|
||||
const jobType = formData.get('job_type');
|
||||
if (jobType === 'guid') {
|
||||
const savedPriority = localStorage.getItem('guidSlotNumbers');
|
||||
if (savedPriority) {
|
||||
formData.append('slot_priority', savedPriority);
|
||||
}
|
||||
}
|
||||
const btn = ipForm.querySelector('button[type="submit"]');
|
||||
const originalHtml = btn.innerHTML;
|
||||
btn.disabled = true;
|
||||
@@ -226,7 +323,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (!res.ok) throw new Error("HTTP " + res.status);
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
|
||||
if (data.job_id) {
|
||||
// 비동기 작업 시작됨 -> 폴링 시작
|
||||
pollProgress(data.job_id);
|
||||
@@ -323,7 +420,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (!wasSelected) toggleSelection(item);
|
||||
}
|
||||
} else if (!e.target.closest('.backup-files-container')) {
|
||||
// 배경 클릭 시 선택 해제? (UX에 따라 결정, 여기선 일단 패스)
|
||||
// 배경 클릭 시 선택 해제? (UX에 따라 결정, 여기선 일단 패스)
|
||||
}
|
||||
});
|
||||
|
||||
@@ -365,7 +462,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
// Sortable 초기화
|
||||
backupContainers.forEach(container => {
|
||||
if (typeof Sortable === 'undefined') return;
|
||||
|
||||
|
||||
new Sortable(container, {
|
||||
group: 'backup-files',
|
||||
animation: 150,
|
||||
@@ -421,23 +518,23 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
target_folder: targetFolder
|
||||
})
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
// 속성 업데이트 (다운로드 링크 등)
|
||||
const btn = item.querySelector('.btn-view-backup');
|
||||
if (btn) btn.setAttribute('data-date', targetFolder);
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
// 속성 업데이트 (다운로드 링크 등)
|
||||
const btn = item.querySelector('.btn-view-backup');
|
||||
if (btn) btn.setAttribute('data-date', targetFolder);
|
||||
|
||||
const link = item.querySelector('a[download]');
|
||||
if (link && window.APP_CONFIG.downloadBaseUrl) {
|
||||
const newHref = window.APP_CONFIG.downloadBaseUrl
|
||||
.replace('PLACEHOLDER_DATE', targetFolder)
|
||||
.replace('PLACEHOLDER_FILE', filename);
|
||||
link.setAttribute('href', newHref);
|
||||
const link = item.querySelector('a[download]');
|
||||
if (link && window.APP_CONFIG.downloadBaseUrl) {
|
||||
const newHref = window.APP_CONFIG.downloadBaseUrl
|
||||
.replace('PLACEHOLDER_DATE', targetFolder)
|
||||
.replace('PLACEHOLDER_FILE', filename);
|
||||
link.setAttribute('href', newHref);
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
});
|
||||
return data;
|
||||
});
|
||||
});
|
||||
|
||||
Promise.all(promises).then(results => {
|
||||
@@ -520,9 +617,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
<div>Slot ${slot}</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
|
||||
// 미리보기 Sortable
|
||||
new Sortable(slotPreview, {
|
||||
new Sortable(slotPreview, {
|
||||
animation: 150,
|
||||
ghostClass: 'sortable-ghost',
|
||||
dragClass: 'sortable-drag',
|
||||
@@ -552,7 +649,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
|
||||
slotNumbersInput.addEventListener('input', updateSlotPreview);
|
||||
|
||||
|
||||
btnClearSlots.addEventListener('click', () => {
|
||||
if (confirm('모두 지우시겠습니까?')) {
|
||||
slotNumbersInput.value = '';
|
||||
@@ -604,7 +701,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
e.preventDefault();
|
||||
const btn = form.querySelector('button[type="submit"]');
|
||||
if (!btn || btn.disabled) return;
|
||||
|
||||
|
||||
const originalContent = btn.innerHTML;
|
||||
|
||||
const executeMove = (overwrite = false) => {
|
||||
@@ -620,48 +717,48 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
},
|
||||
body: JSON.stringify({ overwrite: overwrite })
|
||||
})
|
||||
.then(async response => {
|
||||
const ct = response.headers.get("content-type");
|
||||
if (!ct || !ct.includes("application/json")) {
|
||||
// HTTP 200이지만 HTML이 올 경우 에러나 마찬가지 (로그인 리다이렉트 등)
|
||||
if (response.ok && response.url.includes("login")) {
|
||||
.then(async response => {
|
||||
const ct = response.headers.get("content-type");
|
||||
if (!ct || !ct.includes("application/json")) {
|
||||
// HTTP 200이지만 HTML이 올 경우 에러나 마찬가지 (로그인 리다이렉트 등)
|
||||
if (response.ok && response.url.includes("login")) {
|
||||
window.location.reload();
|
||||
return;
|
||||
}
|
||||
// "MAC 이동 중 오류: HTTP 200" 등 텍스트일 수 있음
|
||||
const txt = await response.text();
|
||||
if (response.ok) {
|
||||
// 성공으로 간주하고 리로드 (가끔 백엔드가 JSON 대신 빈 성공 응답을 줄 때)
|
||||
window.location.reload();
|
||||
return;
|
||||
}
|
||||
throw new Error(txt);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (!data) return; // 위에서 처리됨
|
||||
if (data.requires_confirmation) {
|
||||
showDuplicateModal(data.duplicates, data.duplicate_count);
|
||||
pendingAction = executeMove;
|
||||
resetButton();
|
||||
} else if (data.success) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert(data.error || '작업 실패');
|
||||
resetButton();
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
// HTTP 200 에러 억제 요청 반영
|
||||
if (err.message && err.message.includes("200")) {
|
||||
window.location.reload();
|
||||
return;
|
||||
}
|
||||
// "MAC 이동 중 오류: HTTP 200" 등 텍스트일 수 있음
|
||||
const txt = await response.text();
|
||||
if (response.ok) {
|
||||
// 성공으로 간주하고 리로드 (가끔 백엔드가 JSON 대신 빈 성공 응답을 줄 때)
|
||||
window.location.reload();
|
||||
return;
|
||||
}
|
||||
throw new Error(txt);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (!data) return; // 위에서 처리됨
|
||||
if (data.requires_confirmation) {
|
||||
showDuplicateModal(data.duplicates, data.duplicate_count);
|
||||
pendingAction = executeMove;
|
||||
alert('오류 발생: ' + err);
|
||||
resetButton();
|
||||
} else if (data.success) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert(data.error || '작업 실패');
|
||||
resetButton();
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
// HTTP 200 에러 억제 요청 반영
|
||||
if (err.message && err.message.includes("200")) {
|
||||
window.location.reload();
|
||||
return;
|
||||
}
|
||||
alert('오류 발생: ' + err);
|
||||
resetButton();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const resetButton = () => {
|
||||
@@ -690,7 +787,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
li.innerHTML = `<i class="bi bi-file-earmark text-secondary me-2"></i>${name}`;
|
||||
listEl.appendChild(li);
|
||||
});
|
||||
|
||||
|
||||
if (duplicates.length > limit) {
|
||||
if (moreEl) {
|
||||
moreEl.style.display = 'block';
|
||||
@@ -702,4 +799,226 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
if (dupModal) dupModal.show();
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// 6. 작업 유형 및 프로파일 처리 (process_ips 관련)
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
const jobTypeSelect = document.getElementById('job_type');
|
||||
const profileSelect = document.getElementById('profile');
|
||||
const profileGroup = document.getElementById('profileGroup');
|
||||
const profileDesc = document.getElementById('profileDesc');
|
||||
const legacyScriptGroup = document.getElementById('legacyScriptGroup');
|
||||
const scriptSelectLegacy = document.getElementById('script'); // 기존 스크립트 select
|
||||
|
||||
if (jobTypeSelect) {
|
||||
jobTypeSelect.addEventListener('change', async () => {
|
||||
const jobType = jobTypeSelect.value;
|
||||
|
||||
// 초기화
|
||||
if (profileSelect) {
|
||||
profileSelect.innerHTML = '<option value="">프로파일을 불러오는 중...</option>';
|
||||
profileDesc.textContent = '';
|
||||
}
|
||||
|
||||
if (jobType === 'legacy') {
|
||||
// Legacy 모드
|
||||
if (profileGroup) profileGroup.style.display = 'none';
|
||||
if (legacyScriptGroup) legacyScriptGroup.style.display = 'block';
|
||||
if (scriptSelectLegacy) scriptSelectLegacy.required = true;
|
||||
if (profileSelect) profileSelect.required = false;
|
||||
} else if (['mac', 'server_info', 'guid'].includes(jobType)) {
|
||||
// 신규 모드
|
||||
if (legacyScriptGroup) legacyScriptGroup.style.display = 'none';
|
||||
if (profileGroup) profileGroup.style.display = 'block';
|
||||
if (scriptSelectLegacy) scriptSelectLegacy.required = false;
|
||||
if (profileSelect) profileSelect.required = true;
|
||||
|
||||
// 프로파일 로드
|
||||
try {
|
||||
const res = await fetch(`/api/profiles/${jobType}`);
|
||||
if (!res.ok) throw new Error('프로파일 로드 실패');
|
||||
const profiles = await res.json();
|
||||
|
||||
profileSelect.innerHTML = '<option value="">프로파일을 선택하세요</option>';
|
||||
profiles.forEach(p => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = p.name;
|
||||
opt.textContent = `${p.name} ${p.is_default ? '(기본)' : ''}`;
|
||||
opt.dataset.desc = p.description;
|
||||
profileSelect.appendChild(opt);
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
profileSelect.innerHTML = '<option value="">오류 발생</option>';
|
||||
}
|
||||
} else {
|
||||
// 선택 안함
|
||||
if (legacyScriptGroup) legacyScriptGroup.style.display = 'none';
|
||||
if (profileGroup) profileGroup.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// 프로파일 설명 표시
|
||||
if (profileSelect) {
|
||||
profileSelect.addEventListener('change', () => {
|
||||
const selected = profileSelect.options[profileSelect.selectedIndex];
|
||||
if (selected && selected.dataset.desc) {
|
||||
profileDesc.textContent = selected.dataset.desc;
|
||||
} else {
|
||||
profileDesc.textContent = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// 7. 탭 활성화 동기화 (외부 탭 활성화 시 내부 서브탭도 활성화)
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
const repositoryTab = document.getElementById('repository-tab');
|
||||
if (repositoryTab) {
|
||||
repositoryTab.addEventListener('shown.bs.tab', function () {
|
||||
// repository 탭이 활성화되면 첫 번째 서브탭(pills-backup)도 활성화
|
||||
const firstPillTab = document.getElementById('pills-backup-tab');
|
||||
const firstPillPane = document.getElementById('pills-backup');
|
||||
|
||||
if (firstPillTab && firstPillPane) {
|
||||
// 버튼 활성화
|
||||
document.querySelectorAll('#repoTabs .nav-link').forEach(btn => {
|
||||
btn.classList.remove('active');
|
||||
btn.setAttribute('aria-selected', 'false');
|
||||
});
|
||||
firstPillTab.classList.add('active');
|
||||
firstPillTab.setAttribute('aria-selected', 'true');
|
||||
|
||||
// 패널 활성화
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// 7. 커스텀 탭 전환 로직
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
|
||||
// 메인 탭 전환
|
||||
const mainTabButtons = document.querySelectorAll('#fileTabs button[data-custom-toggle="tab"]');
|
||||
mainTabButtons.forEach(btn => {
|
||||
btn.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
// 모든 탭 버튼 비활성화
|
||||
mainTabButtons.forEach(b => b.classList.remove('active'));
|
||||
// 클릭한 탭 버튼 활성화
|
||||
this.classList.add('active');
|
||||
|
||||
// 모든 탭 패널 숨기기
|
||||
document.querySelectorAll('.custom-tab-panel').forEach(panel => {
|
||||
panel.classList.remove('custom-active');
|
||||
panel.style.display = 'none'; // 인라인 스타일로 강제 숨김
|
||||
});
|
||||
|
||||
// 대상 탭 패널 표시
|
||||
const targetId = this.getAttribute('data-bs-target');
|
||||
const targetPanel = document.querySelector(targetId);
|
||||
if (targetPanel) {
|
||||
targetPanel.classList.add('custom-active');
|
||||
targetPanel.style.display = 'block'; // 인라인 스타일도 변경
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// 서브 탭 전환 (Pills)
|
||||
const subTabButtons = document.querySelectorAll('#repoTabs button[data-custom-toggle="pill"]');
|
||||
subTabButtons.forEach(btn => {
|
||||
btn.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
// 모든 서브 탭 버튼 비활성화
|
||||
subTabButtons.forEach(b => b.classList.remove('active'));
|
||||
// 모든 서브 탭 패널 숨기기
|
||||
document.querySelectorAll('.custom-sub-panel').forEach(panel => {
|
||||
panel.classList.remove('custom-sub-active');
|
||||
panel.classList.add('d-none');
|
||||
panel.style.display = 'none';
|
||||
});
|
||||
|
||||
// subpanel-backup을 명시적으로 한 번 더 숨김
|
||||
const backupPanel = document.getElementById('subpanel-backup');
|
||||
if (backupPanel) {
|
||||
backupPanel.classList.add('d-none');
|
||||
backupPanel.style.setProperty('display', 'none', 'important');
|
||||
}
|
||||
|
||||
// 대상 서브 탭 패널 표시
|
||||
const targetId = this.getAttribute('data-bs-target');
|
||||
const targetPanel = document.querySelector(targetId);
|
||||
if (targetPanel) {
|
||||
targetPanel.classList.add('custom-sub-active');
|
||||
targetPanel.classList.remove('d-none');
|
||||
targetPanel.classList.remove('display-none'); // 혹시 모를 클래스 제거
|
||||
targetPanel.style.setProperty('display', 'block', 'important');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// 8. URL 파라미터 기반 탭 활성화 (페이지네이션 지원)
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
|
||||
// 'backup_page' 파라미터가 있으면 '백업 및 저장소' -> '서버 정보' 탭 활성화
|
||||
if (urlParams.has('backup_page')) {
|
||||
const repoTabBtn = document.getElementById('repository-tab');
|
||||
const backupSubTabBtn = document.getElementById('subpanel-backup-tab');
|
||||
|
||||
if (repoTabBtn) {
|
||||
// 메인 탭 전환 트리거
|
||||
repoTabBtn.click();
|
||||
}
|
||||
|
||||
if (backupSubTabBtn) {
|
||||
// 서브 탭 전환 트리거 (메인 탭 전환 후 실행)
|
||||
setTimeout(() => {
|
||||
backupSubTabBtn.click();
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// 9. 시스템 제어 패널 토글 아이콘 회전
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
const systemControlPanel = document.getElementById('systemControlPanel');
|
||||
const systemControlHint = document.getElementById('systemControlHint');
|
||||
|
||||
if (systemControlPanel) {
|
||||
systemControlPanel.addEventListener('show.bs.collapse', () => {
|
||||
const icon = document.querySelector('button[data-bs-target="#systemControlPanel"] i');
|
||||
if (icon) {
|
||||
icon.classList.remove('bi-chevron-down');
|
||||
icon.classList.add('bi-chevron-up');
|
||||
}
|
||||
if (systemControlHint) {
|
||||
systemControlHint.style.display = 'none';
|
||||
}
|
||||
});
|
||||
systemControlPanel.addEventListener('hide.bs.collapse', () => {
|
||||
const icon = document.querySelector('button[data-bs-target="#systemControlPanel"] i');
|
||||
if (icon) {
|
||||
icon.classList.remove('bi-chevron-up');
|
||||
icon.classList.add('bi-chevron-down');
|
||||
}
|
||||
if (systemControlHint) {
|
||||
systemControlHint.style.display = 'inline';
|
||||
}
|
||||
});
|
||||
|
||||
// 힌트 클릭 시 트리거
|
||||
if (systemControlHint) {
|
||||
systemControlHint.addEventListener('click', () => {
|
||||
const btn = document.querySelector('button[data-bs-target="#systemControlPanel"]');
|
||||
if (btn) btn.click();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user