feat(video-card): show deleting overlay and disable interactions during delete

This commit is contained in:
Yusuf İpek
2026-02-24 17:46:05 +03:00
parent 7f07a271a4
commit 71233f8fe8
+106 -90
View File
@@ -182,99 +182,115 @@ export function VideoCard({ video, projectId, onDeleted }: VideoCardProps) {
return ( return (
<> <>
<Card className="group overflow-hidden transition-colors hover:bg-accent/50 cursor-pointer"> <div className="relative">
<Link href={`/projects/${projectId}/videos/${video.id}`}> <Card
{/* Thumbnail */} className={`group overflow-hidden transition-colors hover:bg-accent/50 cursor-pointer ${
<div className="relative aspect-video bg-muted overflow-hidden"> isDeleting ? 'pointer-events-none opacity-70' : ''
{imgError ? ( }`}
<div className="absolute inset-0 flex flex-col items-center justify-center bg-muted/80"> >
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground mb-2" /> <Link href={`/projects/${projectId}/videos/${video.id}`}>
<span className="text-xs text-muted-foreground font-medium">Processing...</span> {/* Thumbnail */}
</div> <div className="relative aspect-video bg-muted overflow-hidden">
) : ( {imgError ? (
// eslint-disable-next-line @next/next/no-img-element <div className="absolute inset-0 flex flex-col items-center justify-center bg-muted/80">
<img <Loader2 className="h-8 w-8 animate-spin text-muted-foreground mb-2" />
src={`${video.thumbnailUrl?.replace('vz-thumbnail.b-cdn.net', 'vz-965f4f4a-fc1.b-cdn.net')}${retryKey ? `?t=${retryKey}` : ''}`} <span className="text-xs text-muted-foreground font-medium">Processing...</span>
alt={video.title} </div>
className="absolute inset-0 w-full h-full object-cover transition-transform group-hover:scale-105" ) : (
onError={() => { // eslint-disable-next-line @next/next/no-img-element
setImgError(true); <img
// Check again after 10 seconds in case Bunny is still processing src={`${video.thumbnailUrl?.replace('vz-thumbnail.b-cdn.net', 'vz-965f4f4a-fc1.b-cdn.net')}${retryKey ? `?t=${retryKey}` : ''}`}
setTimeout(() => { alt={video.title}
setRetryKey(Date.now()); className="absolute inset-0 w-full h-full object-cover transition-transform group-hover:scale-105"
setImgError(false); onError={() => {
}, 10000); setImgError(true);
}} // Check again after 10 seconds in case Bunny is still processing
/> setTimeout(() => {
)} setRetryKey(Date.now());
{!imgError && ( setImgError(false);
<div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center"> }, 10000);
<Play className="h-12 w-12 text-white" fill="white" /> }}
</div> />
)} )}
<Badge className="absolute bottom-2 right-2 bg-black/70">{video.duration}</Badge> {!imgError && (
</div> <div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center">
</Link> <Play className="h-12 w-12 text-white" fill="white" />
</div>
)}
<Badge className="absolute bottom-2 right-2 bg-black/70">{video.duration}</Badge>
</div>
</Link>
<CardContent className="p-4"> <CardContent className="p-4">
<div className="flex items-start justify-between gap-2"> <div className="flex items-start justify-between gap-2">
<Link href={`/projects/${projectId}/videos/${video.id}`} className="min-w-0 flex-1"> <Link href={`/projects/${projectId}/videos/${video.id}`} className="min-w-0 flex-1">
<h3 className="font-medium truncate">{video.title}</h3> <h3 className="font-medium truncate">{video.title}</h3>
<div className="flex items-center gap-3 mt-1 text-sm text-muted-foreground"> <div className="flex items-center gap-3 mt-1 text-sm text-muted-foreground">
<span className="flex items-center gap-1"> <span className="flex items-center gap-1">
<Badge variant="secondary" className="text-xs"> <Badge variant="secondary" className="text-xs">
v{video.currentVersion} v{video.currentVersion}
</Badge> </Badge>
</span> </span>
<span className="flex items-center gap-1"> <span className="flex items-center gap-1">
<MessageSquare className="h-3.5 w-3.5" /> <MessageSquare className="h-3.5 w-3.5" />
{video.commentCount} {video.commentCount}
</span> </span>
<span className="flex items-center gap-1"> <span className="flex items-center gap-1">
<Clock className="h-3.5 w-3.5" /> <Clock className="h-3.5 w-3.5" />
{video.lastUpdated} {video.lastUpdated}
</span> </span>
</div> </div>
</Link> </Link>
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
className="h-8 w-8 shrink-0" className="h-8 w-8 shrink-0"
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> disabled={isDeleting}
<MoreVertical className="h-4 w-4" /> >
</Button> <MoreVertical className="h-4 w-4" />
</DropdownMenuTrigger> </Button>
<DropdownMenuContent align="end"> </DropdownMenuTrigger>
<DropdownMenuItem asChild> <DropdownMenuContent align="end">
<Link href={`/projects/${projectId}/videos/${video.id}/share`}> <DropdownMenuItem asChild>
<Share2 className="mr-2 h-4 w-4" /> <Link href={`/projects/${projectId}/videos/${video.id}/share`}>
Share <Share2 className="mr-2 h-4 w-4" />
</Link> Share
</DropdownMenuItem> </Link>
<DropdownMenuItem onSelect={() => setShowEditDialog(true)}> </DropdownMenuItem>
<Pencil className="mr-2 h-4 w-4" /> <DropdownMenuItem onSelect={() => setShowEditDialog(true)}>
Edit <Pencil className="mr-2 h-4 w-4" />
</DropdownMenuItem> Edit
<DropdownMenuItem onSelect={() => setShowVersionDialog(true)}> </DropdownMenuItem>
<Plus className="mr-2 h-4 w-4" /> <DropdownMenuItem onSelect={() => setShowVersionDialog(true)}>
Add Version <Plus className="mr-2 h-4 w-4" />
</DropdownMenuItem> Add Version
<DropdownMenuItem </DropdownMenuItem>
className="text-destructive" <DropdownMenuItem
onSelect={() => setShowDeleteDialog(true)} className="text-destructive"
> onSelect={() => setShowDeleteDialog(true)}
<Trash2 className="mr-2 h-4 w-4" /> >
Delete <Trash2 className="mr-2 h-4 w-4" />
</DropdownMenuItem> Delete
</DropdownMenuContent> </DropdownMenuItem>
</DropdownMenu> </DropdownMenuContent>
</DropdownMenu>
</div>
</CardContent>
</Card>
{isDeleting && (
<div className="absolute inset-0 z-10 flex items-center justify-center rounded-xl bg-background/60 backdrop-blur-[1px]">
<div className="flex items-center gap-2 rounded-full border bg-card px-3 py-1.5 text-sm font-medium shadow-sm">
<Loader2 className="h-4 w-4 animate-spin" />
Deleting...
</div>
</div> </div>
</CardContent> )}
</Card> </div>
{/* Edit Dialog */} {/* Edit Dialog */}
<Dialog open={showEditDialog} onOpenChange={setShowEditDialog}> <Dialog open={showEditDialog} onOpenChange={setShowEditDialog}>