{}} onRestore={() => {}} />));
+ expect(screen.getByText("4Gi")).toBeInTheDocument();
+ if (added == null) expect(screen.queryByText(/ added$/)).not.toBeInTheDocument();
+ else expect(screen.getByText(formatted + " added")).toBeInTheDocument();
+ });
+
it("Restore button is enabled when phase=Succeeded with snapshotID", async () => {
const onRestore = vi.fn();
render(
diff --git a/web/src/components/backups/BackupRow.tsx b/web/src/components/backups/BackupRow.tsx
index 4ee37039e..7009aca8e 100644
--- a/web/src/components/backups/BackupRow.tsx
+++ b/web/src/components/backups/BackupRow.tsx
@@ -1,6 +1,7 @@
import { useResourcePermissions, resourceCan, type ResourceTarget } from "@/lib/resourceTarget";
import { Button, Table } from "@heroui/react";
import { formatRelative } from "@/lib/utils";
+import { BackupSize } from "@/components/backups/BackupSize";
import type { Backup } from "@/types";
import { PhaseChip } from "@/components/ui/PhaseChip";
import { useOptionalIntl } from "@/lib/i18n/format";
@@ -31,7 +32,7 @@ export function BackupRow({ backup, showServer, onSelect, onRestore, target, per
- {backup.status?.size ?? intl.formatMessage({ id: "web.common.empty_value", defaultMessage: "—", description: "Shown in place of a value that is not set" })}
+
{formatRelative(backup.status?.completionTime, intl)}
diff --git a/web/src/components/backups/BackupSize.tsx b/web/src/components/backups/BackupSize.tsx
new file mode 100644
index 000000000..d546b0c2a
--- /dev/null
+++ b/web/src/components/backups/BackupSize.tsx
@@ -0,0 +1,13 @@
+import { formatBytes, parseQuantityToBytes } from "@/lib/utils";
+import type { Backup } from "@/types";
+import { useOptionalIntl } from "@/lib/i18n/format";
+
+export function BackupSize({ backup }: { backup: Backup }) {
+ const intl = useOptionalIntl();
+ return <>
+ {backup.status?.size ?? intl.formatMessage({ id: "web.common.empty_value", defaultMessage: "—", description: "Shown in place of a value that is not set" })}
+ {backup.status?.bytesAdded != null &&
+ {intl.formatMessage({ id: "web.backups.bytes_added", defaultMessage: "{size} added", description: "Packed data added to the backup repository by this run. The size includes its unit." }, { size: formatBytes(parseQuantityToBytes(backup.status.bytesAdded)) })}
+
}
+ >;
+}
diff --git a/web/src/routes/tabs/Backups.test.tsx b/web/src/routes/tabs/Backups.test.tsx
index 16b6ef319..195c3d2cb 100644
--- a/web/src/routes/tabs/Backups.test.tsx
+++ b/web/src/routes/tabs/Backups.test.tsx
@@ -169,7 +169,7 @@ describe("BackupsTab", () => {
expect(await screen.findByText(/Last backup/i)).toBeInTheDocument();
expect(screen.getByText(/Next backup/i)).toBeInTheDocument();
expect(screen.getByRole("heading", { name: /Backups/i })).toBeInTheDocument();
- expect(screen.getByText(/Total size/i)).toBeInTheDocument();
+ expect(screen.getByText(/Total logical size/i)).toBeInTheDocument();
});
it("shows warning when multiple schedules exist", async () => {
@@ -350,7 +350,7 @@ describe("BackupsTab", () => {
);
renderWithQuery();
// Total size stat should show combined size
- expect(await screen.findByText(/Total size/i)).toBeInTheDocument();
+ expect(await screen.findByText(/Total logical size/i)).toBeInTheDocument();
});
});
diff --git a/web/src/routes/tabs/Backups.tsx b/web/src/routes/tabs/Backups.tsx
index 52767b66b..a4c16aff9 100644
--- a/web/src/routes/tabs/Backups.tsx
+++ b/web/src/routes/tabs/Backups.tsx
@@ -12,6 +12,7 @@ import { ErrorBanner } from "@/components/backups/ErrorBanner";
import { ScheduleForm } from "@/components/backups/ScheduleForm";
import { RestoreDialog } from "@/components/backups/RestoreDialog";
import { BackupDetailDrawer } from "@/components/backups/BackupDetailDrawer";
+import { BackupSize } from "@/components/backups/BackupSize";
import type { Backup } from "@/types";
import { useOptionalIntl } from "@/lib/i18n/format";
@@ -112,7 +113,7 @@ export function BackupsTab({ name, ns }: { name: string; ns?: string }) {
accent="violet"
/>
}
accent="warning"
@@ -224,7 +225,7 @@ export function BackupsTab({ name, ns }: { name: string; ns?: string }) {
- {b.status?.size ?? intl.formatMessage({ id: "web.common.empty_value", defaultMessage: "—", description: "Shown in place of a value that is not set" })}
+
{formatRelative(b.status?.completionTime, intl)}
diff --git a/web/src/test/handlers.ts b/web/src/test/handlers.ts
index 1186a0f23..33af18cc4 100644
--- a/web/src/test/handlers.ts
+++ b/web/src/test/handlers.ts
@@ -838,6 +838,18 @@ const wsOrigin = typeof window !== "undefined" ? window.location.origin.replace(
export function buildScreenshotHandlers() {
const data = getScreenshotData();
+ const nightlyBackup = makeBackup({
+ metadata: { name: "mc-survival-nightly-0713", namespace: "default" },
+ spec: { serverRef: { name: "mc-survival" } },
+ status: {
+ phase: "Succeeded",
+ startTime: "2026-07-13T00:12:04Z",
+ completionTime: "2026-07-13T00:14:41Z",
+ size: "1.4 GiB",
+ bytesAdded: "14Ki",
+ snapshotID: "a1b2c3d4e5f6",
+ },
+ });
return [
...fleetHandlers,
http.get("/admin/installation", () => HttpResponse.json({ standalone: false, localCluster: true })),
@@ -1238,10 +1250,7 @@ export function buildScreenshotHandlers() {
http.get("/backups", () =>
HttpResponse.json({
items: [
- makeBackup({
- metadata: { name: "mc-survival-nightly-0713", namespace: "default" },
- spec: { serverRef: { name: "mc-survival" } },
- }),
+ nightlyBackup,
makeBackup({
metadata: { name: "mc-survival-nightly-0712", namespace: "default" },
spec: { serverRef: { name: "mc-survival" } },
@@ -1259,14 +1268,9 @@ export function buildScreenshotHandlers() {
if (name === "mc-survival-nightly-0713") {
return HttpResponse.json(
makeBackup({
- metadata: { name, namespace: "default" },
- spec: { serverRef: { name: "mc-survival" } },
+ ...nightlyBackup,
status: {
- phase: "Succeeded",
- startTime: "2026-07-13T00:12:04Z",
- completionTime: "2026-07-13T00:14:41Z",
- size: "1.4 GiB",
- snapshotID: "a1b2c3d4e5f6",
+ ...nightlyBackup.status,
conditions: cookies.e2e_backup_coverage === "recorded"
? [{ type: "Consistency", status: "False", reason: "UnsupportedRawCopy", message: "The game does not support quiesce." }]
: undefined,
diff --git a/web/src/types.ts b/web/src/types.ts
index 2091c1f42..d671d4e51 100644
--- a/web/src/types.ts
+++ b/web/src/types.ts
@@ -697,6 +697,7 @@ export interface Backup {
completionTime?: string;
snapshotID?: string;
size?: string;
+ bytesAdded?: string;
captureManifest?: BackupCaptureManifest;
conditions?: Array<{ type: string; status: string; reason?: string; message?: string }>;
};