Spaces:
Sleeping
Sleeping
| 'use client' | |
| import { useRouter } from 'next/navigation' | |
| import { NotebookResponse } from '@/lib/types/api' | |
| import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' | |
| import { Button } from '@/components/ui/button' | |
| import { Badge } from '@/components/ui/badge' | |
| import { MoreHorizontal, Archive, ArchiveRestore, Trash2, FileText, StickyNote } from 'lucide-react' | |
| import { formatDistanceToNow } from 'date-fns' | |
| import { | |
| DropdownMenu, | |
| DropdownMenuContent, | |
| DropdownMenuItem, | |
| DropdownMenuTrigger, | |
| } from '@/components/ui/dropdown-menu' | |
| import { useUpdateNotebook, useDeleteNotebook } from '@/lib/hooks/use-notebooks' | |
| import { ConfirmDialog } from '@/components/common/ConfirmDialog' | |
| import { useState } from 'react' | |
| interface NotebookCardProps { | |
| notebook: NotebookResponse | |
| } | |
| export function NotebookCard({ notebook }: NotebookCardProps) { | |
| const [showDeleteDialog, setShowDeleteDialog] = useState(false) | |
| const router = useRouter() | |
| const updateNotebook = useUpdateNotebook() | |
| const deleteNotebook = useDeleteNotebook() | |
| const handleArchiveToggle = (e: React.MouseEvent) => { | |
| e.stopPropagation() | |
| updateNotebook.mutate({ | |
| id: notebook.id, | |
| data: { archived: !notebook.archived } | |
| }) | |
| } | |
| const handleDelete = () => { | |
| deleteNotebook.mutate(notebook.id) | |
| setShowDeleteDialog(false) | |
| } | |
| const handleCardClick = () => { | |
| router.push(`/notebooks/${encodeURIComponent(notebook.id)}`) | |
| } | |
| return ( | |
| <> | |
| <Card | |
| className="group card-hover" | |
| onClick={handleCardClick} | |
| style={{ cursor: 'pointer' }} | |
| > | |
| <CardHeader className="pb-3"> | |
| <div className="flex items-start justify-between"> | |
| <div className="flex-1 min-w-0"> | |
| <CardTitle className="text-base truncate group-hover:text-primary transition-colors"> | |
| {notebook.name} | |
| </CardTitle> | |
| {notebook.archived && ( | |
| <Badge variant="secondary" className="mt-1"> | |
| Archived | |
| </Badge> | |
| )} | |
| </div> | |
| <DropdownMenu> | |
| <DropdownMenuTrigger asChild> | |
| <Button | |
| variant="ghost" | |
| size="sm" | |
| className="opacity-0 group-hover:opacity-100 transition-opacity" | |
| onClick={(e) => e.stopPropagation()} | |
| > | |
| <MoreHorizontal className="h-4 w-4" /> | |
| </Button> | |
| </DropdownMenuTrigger> | |
| <DropdownMenuContent align="end" onClick={(e) => e.stopPropagation()}> | |
| <DropdownMenuItem onClick={handleArchiveToggle}> | |
| {notebook.archived ? ( | |
| <> | |
| <ArchiveRestore className="h-4 w-4 mr-2" /> | |
| Unarchive | |
| </> | |
| ) : ( | |
| <> | |
| <Archive className="h-4 w-4 mr-2" /> | |
| Archive | |
| </> | |
| )} | |
| </DropdownMenuItem> | |
| <DropdownMenuItem | |
| onClick={(e) => { | |
| e.stopPropagation() | |
| setShowDeleteDialog(true) | |
| }} | |
| className="text-red-600" | |
| > | |
| <Trash2 className="h-4 w-4 mr-2" /> | |
| Delete | |
| </DropdownMenuItem> | |
| </DropdownMenuContent> | |
| </DropdownMenu> | |
| </div> | |
| </CardHeader> | |
| <CardContent> | |
| <CardDescription className="line-clamp-2 text-sm"> | |
| {notebook.description || 'No description'} | |
| </CardDescription> | |
| <div className="mt-3 text-xs text-muted-foreground"> | |
| Updated {formatDistanceToNow(new Date(notebook.updated), { addSuffix: true })} | |
| </div> | |
| {/* Item counts footer */} | |
| <div className="mt-3 flex items-center gap-1.5 border-t pt-3"> | |
| <Badge variant="outline" className="text-xs flex items-center gap-1 px-1.5 py-0.5 text-primary border-primary/50"> | |
| <FileText className="h-3 w-3" /> | |
| <span>{notebook.source_count}</span> | |
| </Badge> | |
| <Badge variant="outline" className="text-xs flex items-center gap-1 px-1.5 py-0.5 text-primary border-primary/50"> | |
| <StickyNote className="h-3 w-3" /> | |
| <span>{notebook.note_count}</span> | |
| </Badge> | |
| </div> | |
| </CardContent> | |
| </Card> | |
| <ConfirmDialog | |
| open={showDeleteDialog} | |
| onOpenChange={setShowDeleteDialog} | |
| title="Delete Notebook" | |
| description={`Are you sure you want to delete "${notebook.name}"? This action cannot be undone and will delete all sources, notes, and chat sessions.`} | |
| confirmText="Delete" | |
| confirmVariant="destructive" | |
| onConfirm={handleDelete} | |
| /> | |
| </> | |
| ) | |
| } | |