Compare commits
No commits in common. "4078ad509259b752972d6535e5b81ee0867c3438" and "06970b528ce709bd935058f2787994885e27bcdd" have entirely different histories.
4078ad5092
...
06970b528c
@ -997,52 +997,72 @@ class AppsManager {
|
|||||||
|
|
||||||
let tabsHTML = '';
|
let tabsHTML = '';
|
||||||
let contentHTML = '';
|
let contentHTML = '';
|
||||||
|
let firstTab = null;
|
||||||
|
|
||||||
// Sort categories by order
|
// Sort categories by order
|
||||||
const sortedCategories = Object.entries(categories)
|
const sortedCategories = Object.entries(categories)
|
||||||
.sort(([,a], [,b]) => a.order - b.order);
|
.sort(([,a], [,b]) => a.order - b.order);
|
||||||
|
|
||||||
// Render each category's fields up front and keep only the ones that
|
// Find first tab with fields
|
||||||
// actually produced fields. A "hasFields" heuristic used to gate the tabs
|
|
||||||
// separately, but it drifted from what generateConfigFields really emits —
|
|
||||||
// so a category like Network could pass the check yet render empty, leaving
|
|
||||||
// a blank tab whose body just reads "No configuration options available".
|
|
||||||
// Trusting the rendered output keeps tabs and content in lockstep.
|
|
||||||
const renderedCategories = [];
|
|
||||||
for (const [key, category] of sortedCategories) {
|
for (const [key, category] of sortedCategories) {
|
||||||
const content = await this.generateConfigFields(key, appData);
|
|
||||||
if (!content || content.includes('class="no-fields"')) continue;
|
const hasFields = Object.entries(fieldMappings).some(([fieldKey, fieldConfig]) => {
|
||||||
renderedCategories.push({ key, category, content });
|
if (fieldConfig.category === key) {
|
||||||
|
const cfgKey = this.findMatchingCFGKey(fieldKey, appConfig);
|
||||||
|
return cfgKey && appConfig.hasOwnProperty(cfgKey);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasFields) {
|
||||||
|
firstTab = key;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the preferred category if it's one that has fields, else the first.
|
// Use preferred category if available and valid, otherwise use firstTab
|
||||||
const activeTab = (preferredCategory && renderedCategories.some(c => c.key === preferredCategory))
|
const activeTab = preferredCategory && categories[preferredCategory] ? preferredCategory : firstTab;
|
||||||
? preferredCategory
|
|
||||||
: (renderedCategories[0] ? renderedCategories[0].key : null);
|
// Generate tabs and content together
|
||||||
|
for (const [key, category] of sortedCategories) {
|
||||||
for (const { key, category, content } of renderedCategories) {
|
const hasFields = Object.entries(fieldMappings).some(([fieldKey, fieldConfig]) => {
|
||||||
const isActive = key === activeTab ? 'active' : '';
|
if (fieldConfig.category === key) {
|
||||||
|
const cfgKey = this.findMatchingCFGKey(fieldKey, appConfig);
|
||||||
tabsHTML += `
|
return cfgKey && appConfig.hasOwnProperty(cfgKey);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasFields) {
|
||||||
|
const isActive = key === activeTab ? 'active' : '';
|
||||||
|
|
||||||
|
// Generate tab button
|
||||||
|
tabsHTML += `
|
||||||
<button class="tab-button ${isActive}" data-tab="${key}" onclick="appsManager.showTab('${key}')">
|
<button class="tab-button ${isActive}" data-tab="${key}" onclick="appsManager.showTab('${key}')">
|
||||||
<span class="tab-emoji">${category.icon}</span>
|
<span class="tab-emoji">${category.icon}</span>
|
||||||
<span class="tab-name">${category.name}</span>
|
<span class="tab-name">${category.name}</span>
|
||||||
</button>
|
</button>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
contentHTML += `
|
// Generate content panel
|
||||||
|
//// // console.log(`🔧 Generating content for category: ${key}`);
|
||||||
|
const categoryContent = await this.generateConfigFields(key, appData);
|
||||||
|
|
||||||
|
contentHTML += `
|
||||||
<div class="tab-panel ${isActive}" id="panel-${key}">
|
<div class="tab-panel ${isActive}" id="panel-${key}">
|
||||||
<div class="panel-header">
|
<div class="panel-header">
|
||||||
<h4>${category.icon} ${category.name}</h4>
|
<h4>${category.icon} ${category.name}</h4>
|
||||||
<p>${category.description}</p>
|
<p>${category.description}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-fields app-config">
|
<div class="panel-fields app-config">
|
||||||
${content}
|
${categoryContent}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//// // console.log('✅ Tabs and content generated successfully');
|
||||||
return { tabsHTML, contentHTML };
|
return { tabsHTML, contentHTML };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,13 +5,6 @@ class ConfigUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
formatSubcategoryName(subcategoryName) {
|
formatSubcategoryName(subcategoryName) {
|
||||||
// Display-name overrides where the derived title isn't specific enough.
|
|
||||||
// The underlying file/key names (e.g. backup_advanced, CFG_BACKUP_*) are
|
|
||||||
// untouched, so saved values are unaffected.
|
|
||||||
const overrides = {
|
|
||||||
backup_advanced: 'Engine'
|
|
||||||
};
|
|
||||||
if (overrides[subcategoryName]) return overrides[subcategoryName];
|
|
||||||
return subcategoryName.replace(/_/g, ' ').replace(/\b\w/g, function(l) { return l.toUpperCase(); });
|
return subcategoryName.replace(/_/g, ' ').replace(/\b\w/g, function(l) { return l.toUpperCase(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user