From 02cadc18ebe2ef123ccd6530d01d85645f53ecdc Mon Sep 17 00:00:00 2001 From: Cho Young-Hwi Date: Fri, 3 Apr 2026 09:23:14 +0100 Subject: [PATCH 1/3] [#785] Redesign storyline header with Moleskine cover + info panel Replace plain text header with Moleskine book cover (left) + structured info panel (right) layout inspired by Korean web fiction stores. - Desktop: cover left, info right with label-value rows - Mobile: cover centered on top, info below - Reuses Moleskine notebook styling from Reader tab holdings cards - Info panel: title, rating+views, writer with PFP, plots, genre - Market stats grid preserved below Fixes #785 Co-Authored-By: Claude Opus 4.6 (1M context) --- src/app/story/[storylineId]/page.tsx | 109 ++++++++++++++++++--------- 1 file changed, 73 insertions(+), 36 deletions(-) diff --git a/src/app/story/[storylineId]/page.tsx b/src/app/story/[storylineId]/page.tsx index e9d80e78..bed01208 100644 --- a/src/app/story/[storylineId]/page.tsx +++ b/src/app/story/[storylineId]/page.tsx @@ -260,47 +260,84 @@ function StoryHeader({ }) { return (
- {/* Title */} -

- {storyline.title} -

- - {/* Writer */} -
- Writer - {truncateAddress(storyline.writer_address)}}> - - - {storyline.writer_type === 1 && } -
+ {/* Moleskine cover + Info panel */} +
+ {/* Moleskine book cover */} +
+
+
+
+ + {storyline.genre || "Uncategorized"} + +
+
+ + {storyline.title} + +
+
+ + {storyline.plot_count} {storyline.plot_count === 1 ? "plot" : "plots"} + +
+
+
- {/* Stats */} -
- {storyline.plot_count} {storyline.plot_count === 1 ? "plot" : "plots"} - -
+ {/* Info panel */} +
+ {/* Title */} +

+ {storyline.title} +

+ + {/* Rating + Views */} +
+ + · + +
- {/* Badges */} - {(storyline.genre || (storyline.language && storyline.language !== "English")) && ( -
- {storyline.genre && ( - - {storyline.genre} - - )} - {storyline.language && storyline.language !== "English" && ( - - {storyline.language} - - )} + {/* Info rows */} +
+
+ Writer + {truncateAddress(storyline.writer_address)}}> + + + {storyline.writer_type === 1 && } +
+
+ Plots + + {storyline.plot_count} {storyline.plot_count === 1 ? "plot" : "plots"} + +
+
+ Genre + + {storyline.genre || "Uncategorized"} + {storyline.language && storyline.language !== "English" && ( + · {storyline.language} + )} + +
+
- )} - - {/* Rating */} -
-
+ {/* Market stats */} {priceInfo && (
From 4e9db28d4af1b720c77a72cfc8911cf28eb7bab8 Mon Sep 17 00:00:00 2001 From: Cho Young-Hwi Date: Fri, 3 Apr 2026 09:26:00 +0100 Subject: [PATCH 2/3] [#785] Fix conditional separator for unrated storylines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create RatingSummaryWithSeparator component that renders the rating plus trailing dot separator together. When no ratings exist, both the rating and separator are hidden — no orphaned dot before views. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/app/story/[storylineId]/page.tsx | 4 +- src/components/RatingSummaryWithSeparator.tsx | 38 +++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 src/components/RatingSummaryWithSeparator.tsx diff --git a/src/app/story/[storylineId]/page.tsx b/src/app/story/[storylineId]/page.tsx index bed01208..6fde659d 100644 --- a/src/app/story/[storylineId]/page.tsx +++ b/src/app/story/[storylineId]/page.tsx @@ -8,6 +8,7 @@ import { PriceChart } from "../../../components/PriceChart"; import { DonateWidget } from "../../../components/DonateWidget"; import { RatingWidget } from "../../../components/RatingWidget"; import { RatingSummary } from "../../../components/RatingSummary"; +import { RatingSummaryWithSeparator } from "../../../components/RatingSummaryWithSeparator"; import { ShareButtons } from "../../../components/ShareButtons"; import { StoryContent } from "../../../components/StoryContent"; import { ReadingModeWrapper } from "../../../components/ReadingModeWrapper"; @@ -304,8 +305,7 @@ function StoryHeader({ {/* Rating + Views */}
- - · +
diff --git a/src/components/RatingSummaryWithSeparator.tsx b/src/components/RatingSummaryWithSeparator.tsx new file mode 100644 index 00000000..f1f467e7 --- /dev/null +++ b/src/components/RatingSummaryWithSeparator.tsx @@ -0,0 +1,38 @@ +"use client"; + +import { useQuery } from "@tanstack/react-query"; +import { StarDisplay } from "./StarRating"; + +interface RatingsResponse { + average: number; + count: number; +} + +/** + * RatingSummary that includes a trailing separator dot. + * Renders nothing (no dot) when there are no ratings. + */ +export function RatingSummaryWithSeparator({ storylineId }: { storylineId: number }) { + const { data } = useQuery({ + queryKey: ["ratings", storylineId], + queryFn: async () => { + const res = await fetch(`/api/ratings?storylineId=${storylineId}`); + if (!res.ok) throw new Error("Failed to fetch ratings"); + return res.json(); + }, + }); + + if (!data || data.count === 0) return null; + + return ( + <> + + + + {data.average.toFixed(1)} ({data.count}) + + + · + + ); +} From 2d746ed435e8d3004c4943b9d0257c362b128b5d Mon Sep 17 00:00:00 2001 From: Cho Young-Hwi Date: Fri, 3 Apr 2026 09:27:33 +0100 Subject: [PATCH 3/3] [#785] Remove unused RatingSummary import Co-Authored-By: Claude Opus 4.6 (1M context) --- src/app/story/[storylineId]/page.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/story/[storylineId]/page.tsx b/src/app/story/[storylineId]/page.tsx index 6fde659d..a08ef80d 100644 --- a/src/app/story/[storylineId]/page.tsx +++ b/src/app/story/[storylineId]/page.tsx @@ -7,7 +7,6 @@ import { TradingWidget } from "../../../components/TradingWidget"; import { PriceChart } from "../../../components/PriceChart"; import { DonateWidget } from "../../../components/DonateWidget"; import { RatingWidget } from "../../../components/RatingWidget"; -import { RatingSummary } from "../../../components/RatingSummary"; import { RatingSummaryWithSeparator } from "../../../components/RatingSummaryWithSeparator"; import { ShareButtons } from "../../../components/ShareButtons"; import { StoryContent } from "../../../components/StoryContent";