diff --git a/src/components/Admin/ScheduleImport/DiffReviewStep.test.tsx b/src/components/Admin/ScheduleImport/DiffReviewStep.test.tsx
new file mode 100644
index 00000000..1e367a12
--- /dev/null
+++ b/src/components/Admin/ScheduleImport/DiffReviewStep.test.tsx
@@ -0,0 +1,71 @@
+import { describe, expect, it, vi } from "vitest";
+import { render, screen } from "@testing-library/react";
+import { DiffReviewStep } from "./DiffReviewStep";
+import type { DiffResult } from "@/services/scheduleImport/types";
+
+const emptyDiff: DiffResult = {
+ watermark: "0:none",
+ summary: {
+ newArtists: 0,
+ newStages: 0,
+ setsMatched: 0,
+ setsToCreate: 0,
+ setsOrphaned: 0,
+ },
+ newArtistNames: [],
+ cleanOperations: {
+ artistsToCreate: [],
+ stagesToCreate: [],
+ setsToCreate: [],
+ setsToUpdate: [],
+ },
+ conflicts: { stageNameMismatches: [], orphanedSets: [] },
+};
+
+function renderStep(commitError: string | null) {
+ render(
+ ,
+ );
+}
+
+describe("DiffReviewStep", () => {
+ it("shows a dedicated message and disables the primary button when the edition changed", () => {
+ renderStep(
+ "edition_changed_since_analyse: The schedule changed since this review was generated.",
+ );
+
+ expect(
+ screen.getByText("The schedule changed since this review"),
+ ).toBeVisible();
+ expect(screen.queryByText("Retry")).not.toBeInTheDocument();
+ expect(
+ screen.getByRole("button", { name: "Commit to database" }),
+ ).toBeDisabled();
+ });
+
+ it("shows the generic failure message and an enabled Retry for other commit errors", () => {
+ renderStep("Stage Mainstage not found in edition edition-1");
+
+ expect(
+ screen.getByText("Import failed — no changes were saved."),
+ ).toBeVisible();
+ expect(
+ screen.getByText("Stage Mainstage not found in edition edition-1"),
+ ).toBeVisible();
+ expect(screen.getByRole("button", { name: "Retry" })).toBeEnabled();
+ });
+});
diff --git a/src/components/Admin/ScheduleImport/DiffReviewStep.tsx b/src/components/Admin/ScheduleImport/DiffReviewStep.tsx
index bad1893c..a1cfa835 100644
--- a/src/components/Admin/ScheduleImport/DiffReviewStep.tsx
+++ b/src/components/Admin/ScheduleImport/DiffReviewStep.tsx
@@ -6,6 +6,7 @@ import {
type DiffResult,
type StageMismatchResolution,
type OrphanResolution,
+ isEditionChangedError,
} from "@/services/scheduleImport/types";
import type { RevealLevel } from "@/lib/scheduleReveal";
import { DiffSummaryBanner } from "./DiffSummaryBanner";
@@ -53,6 +54,8 @@ export function DiffReviewStep({
const setsToArchive = Object.values(orphanResolutions).filter(
(r) => r === "archive",
).length;
+ const editionChanged =
+ commitError != null && isEditionChangedError(commitError);
return (
@@ -90,8 +93,16 @@ export function DiffReviewStep({
{commitError && (
- Import failed — no changes were saved.
- {commitError}
+
+ {editionChanged
+ ? "The schedule changed since this review"
+ : "Import failed — no changes were saved."}
+
+
+ {editionChanged
+ ? "Someone changed this edition's schedule after Analyse ran. Nothing was applied — click Start over to re-run Analyse against the latest data."
+ : commitError}
+
)}
@@ -99,13 +110,16 @@ export function DiffReviewStep({
-