Merge claude/2
This commit is contained in:
commit
ac07420ef1
@ -16,9 +16,17 @@ if (typeof window.ConfigManager === 'undefined') {
|
||||
window.IPWhitelistManager = this.whitelistManager;
|
||||
}
|
||||
|
||||
async renderConfig(category) {
|
||||
async renderConfig(category, target) {
|
||||
|
||||
const configSection = document.getElementById('config-section');
|
||||
// `target` (an element or an element id) lets an embedder render into its
|
||||
// OWN container instead of the page-global #config-section. The shared
|
||||
// apps-unified-layout keeps a hidden #config-tab > #config-section in the
|
||||
// DOM at all times, so on the Fleet Overview a plain
|
||||
// getElementById('config-section') resolves to THAT stale element (two
|
||||
// nodes share the id) and the form paints where nobody can see it. The
|
||||
// Backups tab's Configuration sub-tab passes its own container id here.
|
||||
const configSection = (typeof target === 'string' ? document.getElementById(target) : target)
|
||||
|| document.getElementById('config-section');
|
||||
if (!configSection) {
|
||||
console.error('ConfigManager: config-section element not found');
|
||||
return;
|
||||
@ -238,7 +246,7 @@ if (typeof window.ConfigManager === 'undefined') {
|
||||
|
||||
} catch (error) {
|
||||
console.error('ConfigManager: Error rendering ' + category + ' config: ', error);
|
||||
configSection.innerHTML = '<div class="error-container"><h3>Error Loading Configuration</h3><p>Failed to load configuration: ' + error.message + '</p><button type="button" class="btn btn-primary" onclick="window.configManager.renderConfig(\'' + category + '\')">Retry</button></div>';
|
||||
configSection.innerHTML = '<div class="error-container"><h3>Error Loading Configuration</h3><p>Failed to load configuration: ' + error.message + '</p><button type="button" class="btn btn-primary" onclick="window.configManager.renderConfig(\'' + category + '\', \'' + configSection.id + '\')">Retry</button></div>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -29,9 +29,14 @@ Object.assign(BackupPage.prototype, {
|
||||
<div class="config-divider"></div>
|
||||
`;
|
||||
|
||||
// Unique id (NOT the page-global "config-section"): the shared
|
||||
// apps-unified-layout keeps a hidden #config-tab > #config-section in the
|
||||
// DOM, so reusing that id would collide and the config form would render
|
||||
// into the wrong (invisible) node. renderConfig() is told to target this
|
||||
// container explicitly.
|
||||
body.innerHTML = `
|
||||
${warningHTML}
|
||||
<div id="config-section" class="backup-embedded-config"></div>
|
||||
<div id="backup-config-section" class="backup-embedded-config"></div>
|
||||
`;
|
||||
|
||||
this.invokeConfigManager();
|
||||
@ -39,7 +44,7 @@ Object.assign(BackupPage.prototype, {
|
||||
async invokeConfigManager(attempt = 0) {
|
||||
if (window.configManager && typeof window.configManager.renderConfig === 'function') {
|
||||
try {
|
||||
await window.configManager.renderConfig('backup');
|
||||
await window.configManager.renderConfig('backup', 'backup-config-section');
|
||||
this.enhanceConfigurationWithPresets();
|
||||
} catch (err) {
|
||||
console.error('Backup configuration render failed:', err);
|
||||
@ -47,7 +52,7 @@ Object.assign(BackupPage.prototype, {
|
||||
return;
|
||||
}
|
||||
if (attempt >= 20) {
|
||||
const sec = document.getElementById('config-section');
|
||||
const sec = document.getElementById('backup-config-section');
|
||||
if (sec) sec.innerHTML = `<div class="backup-empty-state">Configuration system not loaded. Try refreshing the page.</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
Object.assign(BackupPage.prototype, {
|
||||
enhanceEngineDetailsButton() {
|
||||
const selector = '[name="CFG_BACKUP_ENGINE"], [name^="CFG_BACKUP_LOC_"][name$="_ENGINE"]';
|
||||
document.querySelectorAll(`#config-section ${selector}, .backup-location-details ${selector}`).forEach((engineInput) => {
|
||||
document.querySelectorAll(`#backup-config-section ${selector}, .backup-location-details ${selector}`).forEach((engineInput) => {
|
||||
const customSelect = engineInput.closest('.custom-select');
|
||||
const wrapTarget = customSelect || engineInput;
|
||||
const group = wrapTarget.closest('.field-group') || wrapTarget.parentElement;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
Object.assign(BackupPage.prototype, {
|
||||
enhanceConfigurationWithPresets() {
|
||||
this.enhanceEngineDetailsButton();
|
||||
const lastInput = document.querySelector('#config-section [name="CFG_BACKUP_KEEP_LAST"]');
|
||||
const lastInput = document.querySelector('#backup-config-section [name="CFG_BACKUP_KEEP_LAST"]');
|
||||
if (!lastInput) return;
|
||||
|
||||
const section = lastInput.closest('.config-category');
|
||||
|
||||
@ -86,6 +86,19 @@
|
||||
background: rgba(var(--text-rgb), 0.1);
|
||||
}
|
||||
|
||||
/* Busy state while a manual refresh is in flight — spin the arrow icon and
|
||||
lock the button so the click gives visible feedback (refreshAll() + re-render
|
||||
is otherwise silent, which read as "the button does nothing"). `spin` is the
|
||||
global keyframe from core/theme/css/base.css. */
|
||||
.backup-refresh-btn.is-refreshing {
|
||||
pointer-events: none;
|
||||
opacity: 0.75;
|
||||
}
|
||||
.backup-refresh-btn.is-refreshing svg {
|
||||
animation: spin 0.8s linear infinite;
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
.backup-tabpanel {
|
||||
display: none;
|
||||
/* Content inset matching the canonical .tab-panel (5px 24px), so panels
|
||||
|
||||
@ -118,8 +118,16 @@ class BackupPage {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.target.closest('#backup-refresh-btn')) {
|
||||
this.refreshAll().then(() => this.render());
|
||||
const refreshBtn = e.target.closest('#backup-refresh-btn');
|
||||
if (refreshBtn) {
|
||||
// Give the click visible feedback: spin + lock the button until
|
||||
// the refetch + re-render settle. Without this the button looks
|
||||
// dead because refreshAll()/render() paint nothing new on a tab
|
||||
// whose data hasn't changed.
|
||||
if (refreshBtn.classList.contains('is-refreshing')) return;
|
||||
refreshBtn.classList.add('is-refreshing');
|
||||
const done = () => refreshBtn.classList.remove('is-refreshing');
|
||||
this.refreshAll().then(() => this.render()).then(done, done);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user