fix(stats): resolve dashboard initialization race condition and test failure

- Refactor StatisticsManager to return promises from initializeVisualizations and initializeLists
- Update fetchAndRenderList to use the fetchData wrapper for consistent mocking
- Update statistics dashboard test to include mock data for paginated model-usage-list endpoint
This commit is contained in:
Will Miao
2026-03-03 15:08:33 +08:00
parent 8c5d5a8ca0
commit 7ba9b998d3
2 changed files with 37 additions and 9 deletions

View File

@@ -29,7 +29,7 @@ export class StatisticsManager {
await this.loadAllData(); await this.loadAllData();
// Initialize charts and visualizations // Initialize charts and visualizations
this.initializeVisualizations(); await this.initializeVisualizations();
this.initialized = true; this.initialized = true;
} }
@@ -102,7 +102,7 @@ export class StatisticsManager {
return response.json(); return response.json();
} }
initializeVisualizations() { async initializeVisualizations() {
// Initialize metrics cards // Initialize metrics cards
this.renderMetricsCards(); this.renderMetricsCards();
@@ -110,7 +110,7 @@ export class StatisticsManager {
this.initializeCharts(); this.initializeCharts();
// Initialize lists and other components // Initialize lists and other components
this.initializeLists(); await this.initializeLists();
this.renderLargestModelsList(); this.renderLargestModelsList();
this.renderTagCloud(); this.renderTagCloud();
this.renderInsights(); this.renderInsights();
@@ -554,14 +554,14 @@ export class StatisticsManager {
}); });
} }
initializeLists() { async initializeLists() {
const listTypes = [ const listTypes = [
{ type: 'lora', containerId: 'topLorasList' }, { type: 'lora', containerId: 'topLorasList' },
{ type: 'checkpoint', containerId: 'topCheckpointsList' }, { type: 'checkpoint', containerId: 'topCheckpointsList' },
{ type: 'embedding', containerId: 'topEmbeddingsList' } { type: 'embedding', containerId: 'topEmbeddingsList' }
]; ];
listTypes.forEach(({ type, containerId }) => { const promises = listTypes.map(({ type, containerId }) => {
const container = document.getElementById(containerId); const container = document.getElementById(containerId);
if (container) { if (container) {
@@ -573,9 +573,12 @@ export class StatisticsManager {
}); });
// Initial fetch // Initial fetch
this.fetchAndRenderList(type, container); return this.fetchAndRenderList(type, container);
} }
return Promise.resolve();
}); });
await Promise.all(promises);
} }
async fetchAndRenderList(type, container) { async fetchAndRenderList(type, container) {
@@ -591,10 +594,8 @@ export class StatisticsManager {
try { try {
const url = `/api/lm/stats/model-usage-list?type=${type}&sort=${state.sort}&offset=${state.offset}&limit=${state.limit}`; const url = `/api/lm/stats/model-usage-list?type=${type}&sort=${state.sort}&offset=${state.offset}&limit=${state.limit}`;
const response = await fetch(url); const result = await this.fetchData(url);
if (!response.ok) throw new Error('Network response was not ok');
const result = await response.json();
if (result.success) { if (result.success) {
const items = result.data.items; const items = result.data.items;

View File

@@ -107,6 +107,33 @@ describe('Statistics dashboard rendering', () => {
], ],
}, },
}, },
'/api/lm/stats/model-usage-list?type=lora&sort=desc&offset=0&limit=50': {
success: true,
data: {
items: [
{ name: 'Lora A', base_model: 'SDXL', folder: 'loras', usage_count: 10, preview_url: '' },
],
total: 1,
},
},
'/api/lm/stats/model-usage-list?type=checkpoint&sort=desc&offset=0&limit=50': {
success: true,
data: {
items: [
{ name: 'Checkpoint A', base_model: 'SDXL', folder: 'checkpoints', usage_count: 5, preview_url: '' },
],
total: 1,
},
},
'/api/lm/stats/model-usage-list?type=embedding&sort=desc&offset=0&limit=50': {
success: true,
data: {
items: [
{ name: 'Embedding A', base_model: 'SDXL', folder: 'embeddings', usage_count: 7, preview_url: '' },
],
total: 1,
},
},
}; };
const { StatisticsManager } = await import(STATISTICS_MODULE); const { StatisticsManager } = await import(STATISTICS_MODULE);