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' });
|
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(() => {
|
let stats: PrintStats = $derived.by(() => {
|
||||||
const printed = badge_li.filter((b) => (b.print_count ?? 0) >= 1 && b.print_last_datetime);
|
const printed = badge_li.filter((b) => (b.print_count ?? 0) >= 1 && b.print_last_datetime);
|
||||||
if (!printed.length) {
|
if (!printed.length) {
|
||||||
@@ -41,7 +49,7 @@ let stats: PrintStats = $derived.by(() => {
|
|||||||
|
|
||||||
const bucket_ms = bucket_size * 60 * 1000;
|
const bucket_ms = bucket_size * 60 * 1000;
|
||||||
const times = printed
|
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));
|
.filter((t) => !isNaN(t));
|
||||||
if (!times.length) {
|
if (!times.length) {
|
||||||
return { buckets: [], total_printed: printed.length, max_count: 1, span_label: '' };
|
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 start = Math.floor(min_raw / bucket_ms) * bucket_ms;
|
||||||
const end = Math.ceil((max_raw + 1) / bucket_ms) * bucket_ms;
|
const end = Math.ceil((max_raw + 1) / bucket_ms) * bucket_ms;
|
||||||
|
|
||||||
const buckets: Bucket[] = [];
|
const raw_buckets: Omit<Bucket, 'date_label'>[] = [];
|
||||||
let prev_day = '';
|
|
||||||
|
|
||||||
for (let t = start; t < end; t += bucket_ms) {
|
for (let t = start; t < end; t += bucket_ms) {
|
||||||
const in_bucket = printed.filter((b) => {
|
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;
|
return bt >= t && bt < t + bucket_ms;
|
||||||
});
|
});
|
||||||
if (in_bucket.length === 0) continue;
|
if (in_bucket.length === 0) continue;
|
||||||
|
|
||||||
const day = fmt_date(t);
|
raw_buckets.push({
|
||||||
const date_label = day !== prev_day ? day : null;
|
|
||||||
prev_day = day;
|
|
||||||
|
|
||||||
buckets.push({
|
|
||||||
start_ms: t,
|
start_ms: t,
|
||||||
label: fmt_time(t),
|
label: fmt_time(t),
|
||||||
date_label,
|
|
||||||
count: in_bucket.length,
|
count: in_bucket.length,
|
||||||
badges: in_bucket
|
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 max_count = Math.max(...buckets.map((b) => b.count), 1);
|
||||||
const span_label =
|
const span_label =
|
||||||
buckets.length > 0
|
buckets.length > 0
|
||||||
|
|||||||
Reference in New Issue
Block a user