fix: address ui review follow-ups
This commit is contained in:
@@ -381,6 +381,7 @@ export function ReviewReport({
|
||||
const unresolved = unresolvedIndices(drafts).length;
|
||||
const reviewing = review.isReviewing;
|
||||
const hasReport =
|
||||
initialReview !== undefined ||
|
||||
review.state.phase === "done" ||
|
||||
review.state.sections.length > 0 ||
|
||||
seededConflicts.length > 0 ||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import { useEffect, useRef, useState, type ReactNode } from "react";
|
||||
import { ChevronDown } from "lucide-react";
|
||||
|
||||
import { Badge } from "@/components/ui/Badge";
|
||||
@@ -23,10 +23,21 @@ export function ReviewSectionPanel({
|
||||
children,
|
||||
defaultOpen = false,
|
||||
}: ReviewSectionPanelProps) {
|
||||
const [open, setOpen] = useState(defaultOpen);
|
||||
const previousDefaultOpen = useRef(defaultOpen);
|
||||
|
||||
useEffect(() => {
|
||||
if (defaultOpen && !previousDefaultOpen.current) {
|
||||
setOpen(true);
|
||||
}
|
||||
previousDefaultOpen.current = defaultOpen;
|
||||
}, [defaultOpen]);
|
||||
|
||||
return (
|
||||
<details
|
||||
className="group rounded border border-line bg-bg/45"
|
||||
open={defaultOpen}
|
||||
open={open}
|
||||
onToggle={(event) => setOpen(event.currentTarget.open)}
|
||||
>
|
||||
<summary className="flex cursor-pointer list-none items-center justify-between gap-3 px-3 py-2">
|
||||
<span className="min-w-0">
|
||||
|
||||
45
apps/web/lib/review/chapterNav.test.ts
Normal file
45
apps/web/lib/review/chapterNav.test.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { reviewChapterHref, reviewChapterOptions } from "./chapterNav";
|
||||
import type { ChapterEntry } from "@/lib/workbench/chapter";
|
||||
|
||||
describe("reviewChapterHref", () => {
|
||||
it("builds the review route for a chapter number", () => {
|
||||
expect(reviewChapterHref("proj-1", 3)).toBe(
|
||||
"/projects/proj-1/review?chapter=3",
|
||||
);
|
||||
});
|
||||
|
||||
it("always targets the review page (not write)", () => {
|
||||
expect(reviewChapterHref("p", 1)).toContain("/review?chapter=1");
|
||||
});
|
||||
});
|
||||
|
||||
describe("reviewChapterOptions", () => {
|
||||
const chapters: ChapterEntry[] = [
|
||||
{ no: 1, title: "开篇" },
|
||||
{ no: 2 },
|
||||
{ no: 3, title: "转折" },
|
||||
];
|
||||
|
||||
it("merges outline chapters with the current chapter, sorted ascending", () => {
|
||||
const opts = reviewChapterOptions(chapters, 2);
|
||||
expect(opts.map((o) => o.no)).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it("includes the current chapter even when it is outside the outline", () => {
|
||||
const opts = reviewChapterOptions(chapters, 9);
|
||||
expect(opts.map((o) => o.no)).toEqual([1, 2, 3, 9]);
|
||||
});
|
||||
|
||||
it("labels each option with chapter number and optional title", () => {
|
||||
const opts = reviewChapterOptions(chapters, 1);
|
||||
expect(opts[0]).toEqual({ no: 1, label: "第 1 章 · 开篇" });
|
||||
expect(opts[1]).toEqual({ no: 2, label: "第 2 章" });
|
||||
});
|
||||
|
||||
it("returns just the current chapter when there is no outline", () => {
|
||||
const opts = reviewChapterOptions([], 4);
|
||||
expect(opts).toEqual([{ no: 4, label: "第 4 章" }]);
|
||||
});
|
||||
});
|
||||
23
apps/web/lib/review/chapterNav.ts
Normal file
23
apps/web/lib/review/chapterNav.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { ChapterEntry } from "@/lib/workbench/chapter";
|
||||
import { buildChapterEntries } from "@/lib/workbench/chapter";
|
||||
|
||||
export interface ReviewChapterOption {
|
||||
no: number;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export function reviewChapterHref(projectId: string, chapterNo: number): string {
|
||||
return `/projects/${projectId}/review?chapter=${chapterNo}`;
|
||||
}
|
||||
|
||||
export function reviewChapterOptions(
|
||||
chapters: readonly ChapterEntry[],
|
||||
currentChapterNo: number,
|
||||
): ReviewChapterOption[] {
|
||||
return buildChapterEntries(chapters, currentChapterNo).map((chapter) => ({
|
||||
no: chapter.no,
|
||||
label: chapter.title
|
||||
? `第 ${chapter.no} 章 · ${chapter.title}`
|
||||
: `第 ${chapter.no} 章`,
|
||||
}));
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user