fix(badges): print throughput report — descending sort + UTC timezone fix
Buckets now display newest-first. Naive UTC datetime strings from the backend are normalized with a Z suffix before parsing so times display in local browser timezone, matching the badge list. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,14 @@ function fmt_date(ms: number): string {
|
||||
return new Date(ms).toLocaleDateString([], { month: 'short', day: 'numeric' });
|
||||
}
|
||||
|
||||
// Naive UTC strings from the backend have no timezone indicator — append Z so
|
||||
// the browser parses them as UTC and converts to local time, matching the badge list display.
|
||||
function parse_utc_ms(dt: string | null | undefined): number {
|
||||
if (!dt) return NaN;
|
||||
const normalized = dt.match(/Z$|[+-]\d{2}:?\d{2}$/) ? dt : dt + 'Z';
|
||||
return new Date(normalized).getTime();
|
||||
}
|
||||
|
||||
let stats: PrintStats = $derived.by(() => {
|
||||
const printed = badge_li.filter((b) => (b.print_count ?? 0) >= 1 && b.print_last_datetime);
|
||||
if (!printed.length) {
|
||||
@@ -41,7 +49,7 @@ let stats: PrintStats = $derived.by(() => {
|
||||
|
||||
const bucket_ms = bucket_size * 60 * 1000;
|
||||
const times = printed
|
||||
.map((b) => new Date(b.print_last_datetime).getTime())
|
||||
.map((b) => parse_utc_ms(b.print_last_datetime))
|
||||
.filter((t) => !isNaN(t));
|
||||
if (!times.length) {
|
||||
return { buckets: [], total_printed: printed.length, max_count: 1, span_label: '' };
|
||||
@@ -52,29 +60,33 @@ let stats: PrintStats = $derived.by(() => {
|
||||
const start = Math.floor(min_raw / bucket_ms) * bucket_ms;
|
||||
const end = Math.ceil((max_raw + 1) / bucket_ms) * bucket_ms;
|
||||
|
||||
const buckets: Bucket[] = [];
|
||||
let prev_day = '';
|
||||
const raw_buckets: Omit<Bucket, 'date_label'>[] = [];
|
||||
|
||||
for (let t = start; t < end; t += bucket_ms) {
|
||||
const in_bucket = printed.filter((b) => {
|
||||
const bt = new Date(b.print_last_datetime).getTime();
|
||||
const bt = parse_utc_ms(b.print_last_datetime);
|
||||
return bt >= t && bt < t + bucket_ms;
|
||||
});
|
||||
if (in_bucket.length === 0) continue;
|
||||
|
||||
const day = fmt_date(t);
|
||||
const date_label = day !== prev_day ? day : null;
|
||||
prev_day = day;
|
||||
|
||||
buckets.push({
|
||||
raw_buckets.push({
|
||||
start_ms: t,
|
||||
label: fmt_time(t),
|
||||
date_label,
|
||||
count: in_bucket.length,
|
||||
badges: in_bucket
|
||||
});
|
||||
}
|
||||
|
||||
// Newest-first display; re-assign date separators after reversing.
|
||||
raw_buckets.reverse();
|
||||
let prev_day = '';
|
||||
const buckets: Bucket[] = raw_buckets.map((b) => {
|
||||
const day = fmt_date(b.start_ms);
|
||||
const date_label = day !== prev_day ? day : null;
|
||||
prev_day = day;
|
||||
return { ...b, date_label };
|
||||
});
|
||||
|
||||
const max_count = Math.max(...buckets.map((b) => b.count), 1);
|
||||
const span_label =
|
||||
buckets.length > 0
|
||||
|
||||
Reference in New Issue
Block a user