added ETA for Backupjob
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
export function JobStatusPanel({ job }) {
|
export function JobStatusPanel({ job }) {
|
||||||
const percent = Math.round(job?.progress?.percent || 0);
|
const percent = Math.round(job?.progress?.percent || 0);
|
||||||
|
const transfer = transferStats(job);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="min-w-0 overflow-hidden rounded-md border border-zinc-800 bg-zinc-900/70">
|
<section className="min-w-0 overflow-hidden rounded-md border border-zinc-800 bg-zinc-900/70">
|
||||||
@@ -14,6 +15,9 @@ export function JobStatusPanel({ job }) {
|
|||||||
<Info label="Type" value={job?.type || '-'} />
|
<Info label="Type" value={job?.type || '-'} />
|
||||||
<Info label="Current Step" value={job?.currentStep || 'No active job'} />
|
<Info label="Current Step" value={job?.currentStep || 'No active job'} />
|
||||||
<Info label="Progress" value={job ? `${percent}%` : '-'} />
|
<Info label="Progress" value={job ? `${percent}%` : '-'} />
|
||||||
|
<Info label="Transferred" value={transfer.transferred} />
|
||||||
|
<Info label="Rate" value={transfer.rate} />
|
||||||
|
<Info label="ETA" value={transfer.eta} />
|
||||||
<Info label="Started" value={formatTime(job?.startedAt)} />
|
<Info label="Started" value={formatTime(job?.startedAt)} />
|
||||||
<Info label="Finished" value={formatTime(job?.finishedAt)} />
|
<Info label="Finished" value={formatTime(job?.finishedAt)} />
|
||||||
{job?.error ? <Info label="Error" value={job.error} tone="text-red-300" /> : null}
|
{job?.error ? <Info label="Error" value={job.error} tone="text-red-300" /> : null}
|
||||||
@@ -27,6 +31,13 @@ export function JobStatusPanel({ job }) {
|
|||||||
<div className="h-2 overflow-hidden rounded-md bg-zinc-800">
|
<div className="h-2 overflow-hidden rounded-md bg-zinc-800">
|
||||||
<div className="h-full bg-cyan-400 transition-all" style={{ width: `${job ? percent : 0}%` }} />
|
<div className="h-full bg-cyan-400 transition-all" style={{ width: `${job ? percent : 0}%` }} />
|
||||||
</div>
|
</div>
|
||||||
|
{job ? (
|
||||||
|
<div className="mt-3 grid gap-2 text-xs text-zinc-500 sm:grid-cols-3">
|
||||||
|
<Metric label="Transferred" value={transfer.transferred} />
|
||||||
|
<Metric label="Rate" value={transfer.rate} />
|
||||||
|
<Metric label="ETA" value={transfer.eta} />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<pre className="max-h-80 max-w-full overflow-auto rounded-md border border-zinc-800 bg-zinc-950 p-3 text-xs leading-5 text-zinc-300">
|
<pre className="max-h-80 max-w-full overflow-auto rounded-md border border-zinc-800 bg-zinc-950 p-3 text-xs leading-5 text-zinc-300">
|
||||||
{(job?.logs || ['No logs yet.']).join('\n')}
|
{(job?.logs || ['No logs yet.']).join('\n')}
|
||||||
@@ -37,6 +48,15 @@ export function JobStatusPanel({ job }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Metric({ label, value }) {
|
||||||
|
return (
|
||||||
|
<div className="min-w-0">
|
||||||
|
<div className="uppercase tracking-wide">{label}</div>
|
||||||
|
<div className="mt-1 truncate font-mono text-zinc-300">{value}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function Info({ label, value, tone = 'text-zinc-300' }) {
|
function Info({ label, value, tone = 'text-zinc-300' }) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -46,6 +66,49 @@ function Info({ label, value, tone = 'text-zinc-300' }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function transferStats(job) {
|
||||||
|
const currentBytes = Number(job?.progress?.currentBytes || 0);
|
||||||
|
const totalBytes = Number(job?.progress?.totalBytes || 0);
|
||||||
|
const startedAt = job?.startedAt ? new Date(job.startedAt).getTime() : 0;
|
||||||
|
const finishedAt = job?.finishedAt ? new Date(job.finishedAt).getTime() : Date.now();
|
||||||
|
const elapsedSeconds = startedAt ? Math.max(1, (finishedAt - startedAt) / 1000) : 0;
|
||||||
|
const bytesPerSecond = currentBytes && elapsedSeconds ? currentBytes / elapsedSeconds : 0;
|
||||||
|
const remainingBytes = totalBytes && currentBytes ? Math.max(0, totalBytes - currentBytes) : 0;
|
||||||
|
const remainingSeconds = bytesPerSecond && remainingBytes ? remainingBytes / bytesPerSecond : 0;
|
||||||
|
|
||||||
|
return {
|
||||||
|
transferred: currentBytes && totalBytes
|
||||||
|
? `${formatBytes(currentBytes)} / ${formatBytes(totalBytes)}`
|
||||||
|
: currentBytes
|
||||||
|
? formatBytes(currentBytes)
|
||||||
|
: '-',
|
||||||
|
rate: bytesPerSecond ? `${formatBytes(bytesPerSecond)}/s` : '-',
|
||||||
|
eta: remainingSeconds ? formatDuration(remainingSeconds) : job?.status === 'success' ? 'Done' : '-',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatBytes(value) {
|
||||||
|
if (!value) return '-';
|
||||||
|
const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB'];
|
||||||
|
let size = Number(value);
|
||||||
|
let unitIndex = 0;
|
||||||
|
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||||
|
size /= 1024;
|
||||||
|
unitIndex += 1;
|
||||||
|
}
|
||||||
|
return `${size.toFixed(unitIndex === 0 ? 0 : 1)} ${units[unitIndex]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDuration(secondsValue) {
|
||||||
|
const totalSeconds = Math.max(0, Math.round(secondsValue));
|
||||||
|
const hours = Math.floor(totalSeconds / 3600);
|
||||||
|
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
||||||
|
const seconds = totalSeconds % 60;
|
||||||
|
if (hours) return `${hours}h ${minutes}m`;
|
||||||
|
if (minutes) return `${minutes}m ${seconds}s`;
|
||||||
|
return `${seconds}s`;
|
||||||
|
}
|
||||||
|
|
||||||
function statusClass(status) {
|
function statusClass(status) {
|
||||||
if (status === 'success') return 'border-emerald-800 bg-emerald-950 text-emerald-300';
|
if (status === 'success') return 'border-emerald-800 bg-emerald-950 text-emerald-300';
|
||||||
if (status === 'failed') return 'border-red-800 bg-red-950 text-red-300';
|
if (status === 'failed') return 'border-red-800 bg-red-950 text-red-300';
|
||||||
|
|||||||
Reference in New Issue
Block a user