diff --git a/apps/ui/js/usage.js b/apps/ui/js/usage.js index b9b990c..03ba063 100644 --- a/apps/ui/js/usage.js +++ b/apps/ui/js/usage.js @@ -76,6 +76,96 @@ document.addEventListener('DOMContentLoaded', function () { return 'Wk ' + (index + 1); } + function toUtcBucketStart(date, period) { + var bucket = new Date(date.getTime()); + bucket.setUTCMilliseconds(0); + bucket.setUTCSeconds(0); + bucket.setUTCMinutes(0); + bucket.setUTCHours(0); + + if (period === '30d' || period === 'this_month') { + var day = bucket.getUTCDay(); + var offset = day === 0 ? 6 : (day - 1); + bucket.setUTCDate(bucket.getUTCDate() - offset); + return bucket; + } + + if (period === '90d') { + bucket.setUTCDate(1); + return bucket; + } + + return bucket; + } + + function shiftUtcBucket(date, period, amount) { + var shifted = new Date(date.getTime()); + if (period === '30d' || period === 'this_month') { + shifted.setUTCDate(shifted.getUTCDate() + (amount * 7)); + return shifted; + } + if (period === '90d') { + shifted.setUTCMonth(shifted.getUTCMonth() + amount); + return shifted; + } + shifted.setUTCDate(shifted.getUTCDate() + amount); + return shifted; + } + + function bucketKey(date) { + return date.toISOString().slice(0, 19) + 'Z'; + } + + function expectedBucketCount(period, endBucket) { + if (period === '7d') { + return 7; + } + if (period === '30d') { + return 5; + } + if (period === '90d') { + return 4; + } + if (period === 'this_month') { + var firstDay = new Date(Date.UTC(endBucket.getUTCFullYear(), endBucket.getUTCMonth(), 1)); + var firstWeek = toUtcBucketStart(firstDay, period); + var diffMs = endBucket.getTime() - firstWeek.getTime(); + return Math.max(1, Math.floor(diffMs / (7 * 24 * 60 * 60 * 1000)) + 1); + } + return 7; + } + + function normalizeTimeline(timeline, period) { + if (!timeline.length) { + return []; + } + + var byBucket = {}; + timeline.forEach(function(point) { + byBucket[point.bucket_start] = point; + }); + + var latestBucket = timeline.reduce(function(current, point) { + return point.bucket_start > current.bucket_start ? point : current; + }, timeline[0]); + var endBucket = toUtcBucketStart(new Date(latestBucket.bucket_start), period); + var bucketCount = expectedBucketCount(period, endBucket); + var startBucket = shiftUtcBucket(endBucket, period, -(bucketCount - 1)); + var normalized = []; + + for (var index = 0; index < bucketCount; index += 1) { + var bucketDate = shiftUtcBucket(startBucket, period, index); + var key = bucketKey(bucketDate); + normalized.push(byBucket[key] || { + bucket_start: key, + calls_ok: 0, + calls_error: 0, + }); + } + + return normalized; + } + function renderEmpty(title, message) { chartWrap.innerHTML = '