ui: cache derived usage views

This commit is contained in:
a.tolmachev
2026-05-02 15:02:28 +00:00
parent 9a83cee84d
commit 6c98cc6917
+40 -13
View File
@@ -3,6 +3,12 @@ document.addEventListener('DOMContentLoaded', function () {
period: '7d', period: '7d',
workspaceId: null, workspaceId: null,
usage: null, usage: null,
derived: {
localizedOperations: [],
normalizedTimeline: [],
timelineMaxValue: 0,
totalCalls: 0,
},
loading: false, loading: false,
loadError: '', loadError: '',
}; };
@@ -203,6 +209,29 @@ document.addEventListener('DOMContentLoaded', function () {
return normalized; return normalized;
} }
function recomputeDerivedState() {
var operations = state.usage ? (state.usage.operations || []) : [];
var timeline = state.usage ? normalizeTimeline(state.usage.timeline || [], state.period) : [];
var totalCalls = operations.reduce(function (sum, operation) {
return sum + operation.calls_total;
}, 0);
var timelineMaxValue = timeline.reduce(function (maxValue, point) {
return Math.max(maxValue, point.calls_ok + point.calls_error);
}, 0);
state.derived = {
localizedOperations: operations.map(function (operation) {
return {
operation: operation,
localized: localizedUsageOperation(operation),
};
}),
normalizedTimeline: timeline,
timelineMaxValue: timelineMaxValue,
totalCalls: totalCalls,
};
}
function buildEmptyState(title, message, compact) { function buildEmptyState(title, message, compact) {
var empty = element('div', 'empty-state'); var empty = element('div', 'empty-state');
if (compact) { if (compact) {
@@ -263,7 +292,7 @@ document.addEventListener('DOMContentLoaded', function () {
} }
function renderChart() { function renderChart() {
var timeline = state.usage ? normalizeTimeline(state.usage.timeline || [], state.period) : []; var timeline = state.derived.normalizedTimeline;
chartWrap.innerHTML = ''; chartWrap.innerHTML = '';
if (!timeline.length) { if (!timeline.length) {
@@ -277,9 +306,7 @@ document.addEventListener('DOMContentLoaded', function () {
return; return;
} }
var maxValue = Math.max.apply(null, timeline.map(function (point) { var maxValue = state.derived.timelineMaxValue;
return point.calls_ok + point.calls_error;
}));
timeline.forEach(function (point, index) { timeline.forEach(function (point, index) {
var node = chartTemplate.content.cloneNode(true); var node = chartTemplate.content.cloneNode(true);
@@ -301,7 +328,7 @@ document.addEventListener('DOMContentLoaded', function () {
} }
function renderTable() { function renderTable() {
var operations = state.usage ? state.usage.operations : []; var operations = state.derived.localizedOperations;
tableBody.innerHTML = ''; tableBody.innerHTML = '';
if (!operations.length) { if (!operations.length) {
@@ -321,19 +348,16 @@ document.addEventListener('DOMContentLoaded', function () {
return; return;
} }
var totalCalls = operations.reduce(function (sum, operation) { operations.forEach(function (entry) {
return sum + operation.calls_total; var operation = entry.operation;
}, 0); var localizedOperation = entry.localized;
operations.forEach(function (operation) {
var localizedOperation = localizedUsageOperation(operation);
var node = rowTemplate.content.cloneNode(true); var node = rowTemplate.content.cloneNode(true);
var errorRate = operation.calls_total === 0 var errorRate = operation.calls_total === 0
? 0 ? 0
: ((operation.calls_error / operation.calls_total) * 100); : ((operation.calls_error / operation.calls_total) * 100);
var share = totalCalls === 0 var share = state.derived.totalCalls === 0
? 0 ? 0
: ((operation.calls_total / totalCalls) * 100); : ((operation.calls_total / state.derived.totalCalls) * 100);
node.querySelector('.col-name').textContent = localizedOperation.operation_display_name || operation.operation_display_name; node.querySelector('.col-name').textContent = localizedOperation.operation_display_name || operation.operation_display_name;
node.querySelector('.col-op-slug').textContent = operation.operation_name; node.querySelector('.col-op-slug').textContent = operation.operation_name;
@@ -404,8 +428,11 @@ document.addEventListener('DOMContentLoaded', function () {
try { try {
state.usage = await window.CrankApi.getUsageOverview(state.workspaceId, { period: state.period }); state.usage = await window.CrankApi.getUsageOverview(state.workspaceId, { period: state.period });
recomputeDerivedState();
} catch (error) { } catch (error) {
state.loadError = error.message || tKey('usage.error.load'); state.loadError = error.message || tKey('usage.error.load');
state.usage = null;
recomputeDerivedState();
} finally { } finally {
state.loading = false; state.loading = false;
renderUsage(); renderUsage();