diff --git a/app/web/components/EpisodesPage.test.tsx b/app/web/components/EpisodesPage.test.tsx index 7655954..f65c83a 100644 --- a/app/web/components/EpisodesPage.test.tsx +++ b/app/web/components/EpisodesPage.test.tsx @@ -26,6 +26,8 @@ describe("EpisodesPage (#439)", () => { it("lists episodes in reader order with Genesis as Episode 1", async () => { render(); expect(await screen.findByTestId("episodes-page")).toBeInTheDocument(); + expect(screen.getByTestId("episodes-summary")).toHaveTextContent("2 total"); + expect(screen.getByTestId("episodes-summary")).toHaveTextContent("1 ready"); expect(screen.getByTestId("episodes-row-genesis.md")).toHaveTextContent("Episode 1 / Genesis"); expect(screen.getByTestId("episodes-row-plot-01.md")).toHaveTextContent("Episode 2"); }); diff --git a/app/web/components/EpisodesPage.tsx b/app/web/components/EpisodesPage.tsx index 735e7e6..06178ac 100644 --- a/app/web/components/EpisodesPage.tsx +++ b/app/web/components/EpisodesPage.tsx @@ -45,10 +45,34 @@ export function EpisodesPage({ storyName, authFetch, onOpenFile }: EpisodesPageP return
Could not load episodes.
; } + const publishedCount = episodes.filter((ep) => ep.published).length; + const activeCount = episodes.filter((ep) => !ep.published).length; + const blockedCount = episodes.filter((ep) => ep.state === "blocked").length; + const readyCount = episodes.filter((ep) => ep.state === "ready").length; + return (

Episodes

Genesis is Episode 1; each plot file is the next episode.

+
+ + {episodes.length} total + + + {activeCount} active + + + {publishedCount} published + + + {readyCount} ready + + {blockedCount > 0 && ( + + {blockedCount} need fixes + + )} +
{episodes.length === 0 ? (

No episodes yet — write the Genesis to start Episode 1.

diff --git a/app/web/components/StoriesPage.test.tsx b/app/web/components/StoriesPage.test.tsx index c85b644..5d8ba31 100644 --- a/app/web/components/StoriesPage.test.tsx +++ b/app/web/components/StoriesPage.test.tsx @@ -985,6 +985,7 @@ describe("StoriesPage cartoon workflow nav routing (#439)", () => { expect( within(publishCta).getByRole("button", { name: "Next Action" }), ).toBeInTheDocument(); + expect(publishCta.className).toContain("absolute"); expect( within(publishCta).queryByText("No next action available"), ).not.toBeInTheDocument(); diff --git a/app/web/components/StoriesPage.tsx b/app/web/components/StoriesPage.tsx index 6615d2e..1903878 100644 --- a/app/web/components/StoriesPage.tsx +++ b/app/web/components/StoriesPage.tsx @@ -1300,17 +1300,19 @@ export function StoriesPage({ token, authFetch }: StoriesPageProps) {
{!focusedLetteringMode && isCartoonStory && selectedStory && (
- setCartoonView("story-info")} - /> +
+ setCartoonView("story-info")} + /> +
)} {publishProgress && ( diff --git a/app/web/components/StoryInfoPage.tsx b/app/web/components/StoryInfoPage.tsx index bed3624..5b98985 100644 --- a/app/web/components/StoryInfoPage.tsx +++ b/app/web/components/StoryInfoPage.tsx @@ -152,8 +152,37 @@ export function StoryInfoPage({ storyName, authFetch, onSaved }: StoryInfoPagePr return (
-

Story Info

-

These details appear on PlotLink when the story is published.

+
+
+

Story Info

+

These details appear on PlotLink when the story is published.

+
+ + Title {title.trim() ? "ready" : "missing"} + + + Genre {genre ? "set" : "needed"} + + + Language {language ? "set" : "needed"} + + + {cover === "present" ? "Cover ready" : "Cover missing"} + +
+
+
+ + {saved && Saved} + {saveError && {saveError}} +
+
- -
- - {saved && Saved} - {saveError && {saveError}} -
);