Add per-job ETA to print queue

This commit is contained in:
maziggy
2026-08-02 09:53:14 +02:00
3 changed files with 60 additions and 4 deletions
@@ -0,0 +1,27 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { formatETA } from '../../utils/date';
import { formatQueueItemETA } from '../../utils/queueEta';
describe('formatQueueItemETA', () => {
beforeEach(() => {
vi.useFakeTimers();
vi.setSystemTime(new Date('2026-07-31T18:00:00Z'));
});
afterEach(() => {
vi.useRealTimers();
});
it('calculates the ETA from the current time and job duration', () => {
expect(formatQueueItemETA(90 * 60, '24h')).toBe(
formatETA(90, '24h'),
);
});
it('returns null without a usable print duration', () => {
expect(formatQueueItemETA(null)).toBeNull();
expect(formatQueueItemETA(undefined)).toBeNull();
expect(formatQueueItemETA(0)).toBeNull();
expect(formatQueueItemETA(-60)).toBeNull();
});
});
+15 -4
View File
@@ -66,6 +66,7 @@ import { api, ApiError } from '../api/client';
import { PipelineRunsView } from './PipelineRunsPage';
import { type TimeFormat, formatETA, formatDuration, formatRelativeTime, parseUTCDate } from '../utils/date';
import { getBedTypeInfo } from '../utils/bedType';
import { formatQueueItemETA } from '../utils/queueEta';
import type { PrintQueueItem, PrintQueueBulkUpdate, Permission, CalibrationMode } from '../api/client';
import { Card } from '../components/Card';
import { Button } from '../components/Button';
@@ -598,10 +599,20 @@ function SortableQueueItem({
</span>
</span>
{item.print_time_seconds && (
<span className="flex items-center gap-1 sm:gap-1.5">
<Timer className="w-3 h-3 sm:w-3.5 sm:h-3.5" />
{formatDuration(item.print_time_seconds)}
</span>
<>
<span className="flex items-center gap-1 sm:gap-1.5">
<Timer className="w-3 h-3 sm:w-3.5 sm:h-3.5" />
{formatDuration(item.print_time_seconds)}
</span>
{!item.waiting_reason && (
<span
className="text-bambu-green font-medium"
title={t('printers.estimatedCompletion')}
>
ETA {formatQueueItemETA(item.print_time_seconds, timeFormat, t)}
</span>
)}
</>
)}
{item.filament_used_grams && (
<span className="flex items-center gap-1 sm:gap-1.5">
+18
View File
@@ -0,0 +1,18 @@
import { formatETA, type TimeFormat } from './date';
/**
* Formats the estimated completion time for a queue item if it were
* started at the current time.
*
* This is deliberately a per-job estimate rather than a cumulative
* queue forecast.
*/
export function formatQueueItemETA(
printTimeSeconds: number | null | undefined,
timeFormat: TimeFormat = 'system',
t?: Parameters<typeof formatETA>[2],
): string | null {
if (printTimeSeconds == null || printTimeSeconds <= 0) return null;
return formatETA(printTimeSeconds / 60, timeFormat, t);
}