Files
NextEdu/src/modules/textbooks/components/knowledge-point-panel.tsx
SpecialX 57807def37 完整性更新
现在已经实现了大部分基础功能
2026-01-08 11:14:03 +08:00

164 lines
5.9 KiB
TypeScript

"use client"
import { useState } from "react"
import { useRouter } from "next/navigation"
import { Card, CardContent } from "@/shared/components/ui/card"
import { Button } from "@/shared/components/ui/button"
import { Tag, Trash2 } from "lucide-react"
import { KnowledgePoint } from "../types"
import { CreateKnowledgePointDialog } from "./create-knowledge-point-dialog"
import { deleteKnowledgePointAction } from "../actions"
import { toast } from "sonner"
import { ScrollArea } from "@/shared/components/ui/scroll-area"
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/shared/components/ui/alert-dialog"
interface KnowledgePointPanelProps {
knowledgePoints: KnowledgePoint[]
selectedChapterId: string | null
textbookId: string
}
export function KnowledgePointPanel({
knowledgePoints,
selectedChapterId,
textbookId
}: KnowledgePointPanelProps) {
const router = useRouter()
const [showDeleteDialog, setShowDeleteDialog] = useState(false)
const [deleteTarget, setDeleteTarget] = useState<KnowledgePoint | null>(null)
const [isDeleting, setIsDeleting] = useState(false)
const requestDelete = (kp: KnowledgePoint) => {
setDeleteTarget(kp)
setShowDeleteDialog(true)
}
const handleDelete = async () => {
if (!deleteTarget) return
setIsDeleting(true)
try {
const result = await deleteKnowledgePointAction(deleteTarget.id, textbookId)
if (result.success) {
toast.success(result.message)
setShowDeleteDialog(false)
setDeleteTarget(null)
router.refresh()
} else {
toast.error(result.message)
}
} catch {
toast.error("Failed to delete knowledge point")
} finally {
setIsDeleting(false)
}
}
// Filter KPs for the selected chapter
const chapterKPs = selectedChapterId
? knowledgePoints.filter(kp => kp.chapterId === selectedChapterId)
: []
return (
<div className="h-full flex flex-col space-y-4">
<div className="flex items-center justify-between px-2">
<h3 className="font-semibold flex items-center gap-2">
<Tag className="h-4 w-4" />
Knowledge Points
</h3>
{selectedChapterId && (
<CreateKnowledgePointDialog
chapterId={selectedChapterId}
textbookId={textbookId}
/>
)}
</div>
<ScrollArea className="flex-1 min-h-0 -mx-2 px-2">
{selectedChapterId ? (
chapterKPs.length > 0 ? (
<div className="space-y-3">
{chapterKPs.map((kp) => (
<Card key={kp.id} className="relative group">
<CardContent className="p-3">
<div className="flex justify-between items-start gap-2">
<div className="space-y-1">
<div className="font-medium text-sm leading-tight">
{kp.name}
</div>
{kp.description && (
<p className="text-xs text-muted-foreground line-clamp-2">
{kp.description}
</p>
)}
</div>
<Button
variant="ghost"
size="icon"
className="h-6 w-6 opacity-100 md:opacity-0 md:group-hover:opacity-100 transition-opacity text-destructive hover:text-destructive hover:bg-destructive/10 -mt-1 -mr-1"
onClick={() => requestDelete(kp)}
>
<Trash2 className="h-3.5 w-3.5" />
</Button>
</div>
</CardContent>
</Card>
))}
</div>
) : (
<div className="text-sm text-muted-foreground text-center py-8 border rounded-md border-dashed bg-muted/30">
No knowledge points linked to this chapter yet.
</div>
)
) : (
<div className="text-sm text-muted-foreground text-center py-8">
Select a chapter to manage its knowledge points.
</div>
)}
</ScrollArea>
<AlertDialog
open={showDeleteDialog}
onOpenChange={(open) => {
if (isDeleting) return
setShowDeleteDialog(open)
if (!open) setDeleteTarget(null)
}}
>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Delete knowledge point?</AlertDialogTitle>
<AlertDialogDescription>
{deleteTarget ? (
<>
This will permanently delete <span className="font-medium text-foreground">{deleteTarget.name}</span>.
</>
) : (
"This will permanently delete the selected knowledge point."
)}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={isDeleting}>Cancel</AlertDialogCancel>
<AlertDialogAction
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
onClick={handleDelete}
disabled={isDeleting}
>
{isDeleting ? "Deleting..." : "Delete"}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
)
}