Auth = window . Auth || {};
// Colors
colors = ({
navy : '#2C3E50' ,
teal : '#16A085' ,
orange : '#E67E22' ,
gray : '#7F8C8D'
})
// Check authentication and admin status
currentUser = {
const user = Auth. getCurrentUser ();
if (! user) return null ;
const isAdmin = Auth. isUserAdmin ();
return { ... user, isAdmin };
}
// Load content inventory from gallery indices
contentInventory = {
if (! currentUser || ! currentUser. isAdmin ) {
return null ;
}
try {
// Load all gallery indices
const [gamesIndex, simsIndex, labsIndex, checksIndex, squadIndex] = await Promise . all ([
fetch ('/assets/data/games-master-index.json' ). then (r => r. json ()). catch (() => ({ galleries : {} })),
fetch ('/assets/data/simulations-master-index.json' ). then (r => r. json ()). catch (() => ({ galleries : {} })),
fetch ('/assets/data/labs-master-index.json' ). then (r => r. json ()). catch (() => ({ galleries : {} })),
fetch ('/assets/data/knowledge-checks-master-index.json' ). then (r => r. json ()). catch (() => ({ galleries : {} })),
fetch ('/assets/data/sensor-squad-master-index.json' ). then (r => r. json ()). catch (() => ({ galleries : {} }))
]);
// Aggregate by chapter
const chapterMap = new Map ();
// Helper to add content to chapter
const addToChapter = (chapterPath, type) => {
if (! chapterMap. has (chapterPath)) {
chapterMap. set (chapterPath, {
chapter : chapterPath,
games : 0 ,
simulations : 0 ,
labs : 0 ,
knowledgeChecks : 0 ,
sensorSquad : 0
});
}
const entry = chapterMap. get (chapterPath);
entry[type]++;
};
// Process games
Object . values (gamesIndex. galleries || {}). forEach (gallery => {
(gallery. items || []). forEach (item => {
if (item. chapter_path ) {
addToChapter (item. chapter_path , 'games' );
}
});
});
// Process simulations
Object . values (simsIndex. galleries || {}). forEach (gallery => {
(gallery. items || []). forEach (item => {
if (item. chapter_path ) {
addToChapter (item. chapter_path , 'simulations' );
}
});
});
// Process labs
Object . values (labsIndex. galleries || {}). forEach (gallery => {
(gallery. items || []). forEach (item => {
if (item. chapter_path ) {
addToChapter (item. chapter_path , 'labs' );
}
});
});
// Process knowledge checks
Object . values (checksIndex. galleries || {}). forEach (gallery => {
(gallery. items || []). forEach (item => {
if (item. chapter_path ) {
addToChapter (item. chapter_path , 'knowledgeChecks' );
}
});
});
// Process Sensor Squad
Object . values (squadIndex. galleries || {}). forEach (gallery => {
(gallery. items || []). forEach (item => {
if (item. chapter_path ) {
addToChapter (item. chapter_path , 'sensorSquad' );
}
});
});
// Convert to array and sort by chapter name
const inventory = Array . from (chapterMap. values ())
. sort ((a, b) => a. chapter . localeCompare (b. chapter ));
// Calculate totals
const totals = inventory. reduce ((acc, item) => ({
games : acc. games + item. games ,
simulations : acc. simulations + item. simulations ,
labs : acc. labs + item. labs ,
knowledgeChecks : acc. knowledgeChecks + item. knowledgeChecks ,
sensorSquad : acc. sensorSquad + item. sensorSquad
}), { games : 0 , simulations : 0 , labs : 0 , knowledgeChecks : 0 , sensorSquad : 0 });
return {
inventory,
totals,
chaptersWithContent : inventory. length ,
chaptersWithGaps : inventory. filter (item =>
item. games === 0 || item. labs === 0 || item. knowledgeChecks === 0
). length
};
} catch (error) {
console . error ('Error loading content inventory:' , error);
return null ;
}
}
// Filter state
viewState = ({ search : '' , sortBy : 'chapter' , filterType : 'all' })
// Filtered and sorted inventory
filteredInventory = {
if (! contentInventory) return [];
let filtered = contentInventory. inventory ;
// Apply search filter
if (viewState. search ) {
const searchLower = viewState. search . toLowerCase ();
filtered = filtered. filter (item =>
item. chapter . toLowerCase (). includes (searchLower)
);
}
// Apply type filter
if (viewState. filterType !== 'all' ) {
filtered = filtered. filter (item => {
switch (viewState. filterType ) {
case 'with-gaps' :
return item. games === 0 || item. labs === 0 || item. knowledgeChecks === 0 ;
case 'complete' :
return item. games > 0 && item. labs > 0 && item. knowledgeChecks > 0 ;
case 'games-only' :
return item. games > 0 ;
case 'labs-only' :
return item. labs > 0 ;
case 'checks-only' :
return item. knowledgeChecks > 0 ;
default :
return true ;
}
});
}
// Apply sorting
filtered. sort ((a, b) => {
switch (viewState. sortBy ) {
case 'chapter' :
return a. chapter . localeCompare (b. chapter );
case 'games' :
return b. games - a. games ;
case 'simulations' :
return b. simulations - a. simulations ;
case 'labs' :
return b. labs - a. labs ;
case 'checks' :
return b. knowledgeChecks - a. knowledgeChecks ;
case 'total' :
return (b. games + b. simulations + b. labs + b. knowledgeChecks ) -
(a. games + a. simulations + a. labs + a. knowledgeChecks );
default :
return 0 ;
}
});
return filtered;
}