baveshraam's picture
FIX: SurrealDB 2.0 migration syntax and Frontend/CORS link
f871fed
'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}
/>
</>
)
}