fix: третье место самоотравления ASSIGNED_CATEGORIES + сведение в одну функцию

applyConfigTypeToTabs() тоже пересобирал ASSIGNED_CATEGORIES из всех
вкладок, включая "other", и вызывался прямо внутри applyServerSettings —
из-за этого предыдущий фикс (два других места) перезатирался сразу же,
и SYS всё ещё не появлялся во вкладке «Прочее».

Свёл все 4 места пересчёта ASSIGNED_CATEGORIES в одну функцию
computeAssignedCategories(), которая по определению исключает "other" —
это устраняет класс бага целиком, а не по одному вхождению.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Mikhail Chusavitin
2026-07-30 18:21:09 +03:00
co-authored by Claude Sonnet 5
parent dd70b6065b
commit a321dab448
+19 -15
View File
@@ -474,9 +474,17 @@ let TAB_CONFIG = {
} }
}; };
let ASSIGNED_CATEGORIES = Object.values(TAB_CONFIG) // Categories belonging to any real tab (everything except "other").
.flatMap(t => t.categories) // "other" is defined as "whatever isn't in this set" — it must never
// contribute to it, or its own contents would exclude themselves.
function computeAssignedCategories() {
return Object.entries(TAB_CONFIG)
.filter(([key]) => key !== 'other')
.flatMap(([, t]) => t._allCategories || t.categories)
.map(c => ciStr(c)); .map(c => ciStr(c));
}
let ASSIGNED_CATEGORIES = computeAssignedCategories();
// State // State
let configUUID = '{{.ConfigUUID}}'; let configUUID = '{{.ConfigUUID}}';
@@ -822,19 +830,16 @@ async function loadCategoriesFromAPI() {
categoryOrderMap[ciStr(cat.code)] = cat.display_order; categoryOrderMap[ciStr(cat.code)] = cat.display_order;
}); });
// Build list of unassigned categories // Build list of unassigned categories (excludes "other" itself, so
const knownCodes = Object.values(TAB_CONFIG) // this stays correct even if it's re-run after "other" was already
.flatMap(t => t.categories) // populated by a previous call).
.map(c => ciStr(c)); const knownCodes = computeAssignedCategories();
const unassignedCategories = cats const unassignedCategories = cats
.filter(cat => !knownCodes.includes(ciStr(cat.code))) .filter(cat => !knownCodes.includes(ciStr(cat.code)))
.sort((a, b) => a.display_order - b.display_order) .sort((a, b) => a.display_order - b.display_order)
.map(cat => cat.code); .map(cat => cat.code);
// Rebuild ASSIGNED_CATEGORIES from known (non-"other") tabs before
// populating "other", so unassigned categories aren't immediately
// excluded from the "other" tab by their own assignment.
ASSIGNED_CATEGORIES = knownCodes; ASSIGNED_CATEGORIES = knownCodes;
// Update "other" tab with unassigned categories // Update "other" tab with unassigned categories
@@ -886,7 +891,7 @@ function applyServerSettings(settings) {
// before restoring .other — otherwise categories already parked in // before restoring .other — otherwise categories already parked in
// .other (e.g. by loadCategoriesFromAPI) get counted as "assigned" // .other (e.g. by loadCategoriesFromAPI) get counted as "assigned"
// and immediately excluded from the "other" tab again. // and immediately excluded from the "other" tab again.
ASSIGNED_CATEGORIES = Object.values(TAB_CONFIG).flatMap(t => t.categories).map(c => ciStr(c)); ASSIGNED_CATEGORIES = computeAssignedCategories();
TAB_CONFIG.other = otherTab || { categories: [], singleSelect: false, label: 'Other' }; TAB_CONFIG.other = otherTab || { categories: [], singleSelect: false, label: 'Other' };
} }
@@ -1398,11 +1403,10 @@ function applyConfigTypeToTabs() {
} }
}); });
// Rebuild assigned categories index using the full static list (_allCategories), // Rebuild assigned categories index using the full static list
// not the filtered one — hidden categories still belong to their tab, not to Other. // (_allCategories), not the filtered one — hidden categories still
ASSIGNED_CATEGORIES = Object.values(TAB_CONFIG) // belong to their tab, not to Other.
.flatMap(t => t._allCategories || t.categories) ASSIGNED_CATEGORIES = computeAssignedCategories();
.map(c => ciStr(c));
} }
function updateTabVisibility() { function updateTabVisibility() {