139 lines
5.7 KiB
TypeScript
139 lines
5.7 KiB
TypeScript
"use client"
|
|
|
|
import { useState } from "react"
|
|
import ReactMarkdown from "react-markdown"
|
|
import remarkBreaks from "remark-breaks"
|
|
import remarkGfm from "remark-gfm"
|
|
import { Chapter, KnowledgePoint } from "../types"
|
|
import { ChapterSidebarList } from "./chapter-sidebar-list"
|
|
import { KnowledgePointPanel } from "./knowledge-point-panel"
|
|
import { ScrollArea } from "@/shared/components/ui/scroll-area"
|
|
import { Button } from "@/shared/components/ui/button"
|
|
import { Edit2, Save } from "lucide-react"
|
|
import { CreateChapterDialog } from "./create-chapter-dialog"
|
|
import { updateChapterContentAction } from "../actions"
|
|
import { toast } from "sonner"
|
|
import { Textarea } from "@/shared/components/ui/textarea"
|
|
|
|
interface TextbookContentLayoutProps {
|
|
chapters: Chapter[]
|
|
knowledgePoints: KnowledgePoint[]
|
|
textbookId: string
|
|
}
|
|
|
|
export function TextbookContentLayout({ chapters, knowledgePoints, textbookId }: TextbookContentLayoutProps) {
|
|
const [selectedChapter, setSelectedChapter] = useState<Chapter | null>(null)
|
|
const [isEditing, setIsEditing] = useState(false)
|
|
const [editContent, setEditContent] = useState("")
|
|
const [isSaving, setIsSaving] = useState(false)
|
|
|
|
// Sync edit content when selection changes
|
|
const handleSelectChapter = (chapter: Chapter) => {
|
|
setSelectedChapter(chapter)
|
|
setEditContent(chapter.content || "")
|
|
setIsEditing(false)
|
|
}
|
|
|
|
const handleSaveContent = async () => {
|
|
if (!selectedChapter) return
|
|
setIsSaving(true)
|
|
const result = await updateChapterContentAction(selectedChapter.id, editContent, textbookId)
|
|
setIsSaving(false)
|
|
|
|
if (result.success) {
|
|
toast.success(result.message)
|
|
setIsEditing(false)
|
|
setSelectedChapter((prev) => (prev ? { ...prev, content: editContent } : prev))
|
|
} else {
|
|
toast.error(result.message)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="grid grid-cols-12 gap-6 h-[calc(100vh-140px)]">
|
|
{/* Left Sidebar: TOC (3 cols) */}
|
|
<div className="col-span-3 border-r pr-6 flex flex-col h-full">
|
|
<div className="flex items-center justify-between mb-4 px-2">
|
|
<h3 className="font-semibold">Chapters</h3>
|
|
<CreateChapterDialog textbookId={textbookId} />
|
|
</div>
|
|
<ScrollArea className="flex-1 px-2">
|
|
<ChapterSidebarList
|
|
chapters={chapters}
|
|
selectedChapterId={selectedChapter?.id}
|
|
onSelectChapter={handleSelectChapter}
|
|
textbookId={textbookId}
|
|
/>
|
|
</ScrollArea>
|
|
</div>
|
|
|
|
{/* Middle: Content Viewer/Editor (6 cols) */}
|
|
<div className="col-span-6 flex flex-col h-full px-2 min-h-0">
|
|
{selectedChapter ? (
|
|
<>
|
|
<div className="flex items-center justify-between mb-4 pb-2 border-b">
|
|
<h2 className="text-xl font-bold tracking-tight">{selectedChapter.title}</h2>
|
|
<div className="flex gap-2">
|
|
{isEditing ? (
|
|
<>
|
|
<Button size="sm" variant="ghost" onClick={() => setIsEditing(false)} disabled={isSaving}>
|
|
Cancel
|
|
</Button>
|
|
<Button size="sm" onClick={handleSaveContent} disabled={isSaving}>
|
|
<Save className="mr-2 h-4 w-4" />
|
|
{isSaving ? "Saving..." : "Save"}
|
|
</Button>
|
|
</>
|
|
) : (
|
|
<Button size="sm" variant="outline" onClick={() => setIsEditing(true)}>
|
|
<Edit2 className="mr-2 h-4 w-4" />
|
|
Edit Content
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<ScrollArea className="flex-1 min-h-0">
|
|
<div className="p-4 min-h-full">
|
|
{isEditing ? (
|
|
<Textarea
|
|
className="min-h-[500px] font-mono text-sm"
|
|
value={editContent}
|
|
onChange={(e) => setEditContent(e.target.value)}
|
|
placeholder="# Write markdown content here..."
|
|
/>
|
|
) : (
|
|
<div className="prose prose-sm dark:prose-invert max-w-none">
|
|
{selectedChapter.content ? (
|
|
<ReactMarkdown remarkPlugins={[remarkGfm, remarkBreaks]}>
|
|
{selectedChapter.content}
|
|
</ReactMarkdown>
|
|
) : (
|
|
<div className="text-muted-foreground italic py-8 text-center">
|
|
No content available. Click edit to add content.
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</ScrollArea>
|
|
</>
|
|
) : (
|
|
<div className="h-full flex items-center justify-center text-muted-foreground">
|
|
Select a chapter from the left sidebar to view its content.
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Right Sidebar: Knowledge Points (3 cols) */}
|
|
<div className="col-span-3 border-l pl-6 flex flex-col h-full">
|
|
<KnowledgePointPanel
|
|
knowledgePoints={knowledgePoints}
|
|
selectedChapterId={selectedChapter?.id || null}
|
|
textbookId={textbookId}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|