Files
Andrew S Erwin b9721ff13f
release gate / check (push) Failing after 8m35s
initial commit on Federted servers
2026-10-19 16:23:55 -05:00

102 lines
3.8 KiB
TypeScript

'use client'
import { ExternalLink, Calendar as CalendarIcon, Clock } from 'lucide-react'
import { Badge } from '@/components/ui/badge'
import { cn } from '@/lib/utils'
import type { Bookmark } from '@/components/app-shell'
interface BookmarkCardProps {
bookmark: Bookmark
onClick?: () => void
compact?: boolean
}
export function BookmarkCard({ bookmark, onClick, compact }: BookmarkCardProps) {
const domain = (() => { try { return new URL(bookmark?.url ?? '').hostname } catch { return '' } })()
const tags = bookmark?.tags ?? []
return (
<div
onClick={onClick}
className={cn(
'group relative flex cursor-pointer flex-col rounded-lg border bg-card transition-all duration-normal hover:shadow-md hover:-translate-y-0.5 hover:border-primary/20 overflow-hidden',
compact ? 'p-3' : 'p-0'
)}
>
{!compact && bookmark?.ogImage && (
<div className="relative w-full aspect-video bg-muted">
<img
src={bookmark.ogImage}
alt={bookmark?.title ?? 'Bookmark'}
referrerPolicy="no-referrer"
className="object-cover w-full h-full"
onError={(e: any) => { e.target.style.display = 'none' }}
/>
</div>
)}
<div className={cn('flex flex-col gap-2', compact ? '' : 'p-4')}>
<div className="flex items-start gap-2">
{bookmark?.favicon && (
<img
src={bookmark.favicon}
alt=""
referrerPolicy="no-referrer"
className="h-4 w-4 mt-0.5 rounded shrink-0"
onError={(e: any) => { e.target.style.display = 'none' }}
/>
)}
<div className="min-w-0 flex-1">
<h3 className="text-sm font-medium leading-tight truncate">
{bookmark?.title ?? bookmark?.url ?? 'Untitled'}
</h3>
<p className="text-xs text-muted-foreground truncate mt-0.5">{domain}</p>
</div>
<a
href={bookmark?.url ?? '#'}
target="_blank"
rel="noopener noreferrer"
onClick={(e: React.MouseEvent) => e.stopPropagation()}
className="shrink-0 opacity-0 group-hover:opacity-100 transition-opacity"
>
<ExternalLink className="h-4 w-4 text-muted-foreground hover:text-primary" />
</a>
</div>
{!compact && bookmark?.ogDescription && (
<p className="text-xs text-muted-foreground line-clamp-2">{bookmark.ogDescription}</p>
)}
<div className="flex flex-wrap items-center gap-1.5">
{tags.slice(0, 3).map((bt: any) => (
<Badge
key={bt?.tag?.id}
variant="secondary"
className="text-[10px] px-1.5 py-0"
style={{ backgroundColor: `${bt?.tag?.color ?? '#6366f1'}20`, color: bt?.tag?.color ?? '#6366f1' }}
>
{bt?.tag?.name ?? ''}
</Badge>
))}
{tags.length > 3 && (
<span className="text-[10px] text-muted-foreground">+{tags.length - 3}</span>
)}
</div>
{(bookmark?.dueDate || bookmark?.alertAt) && (
<div className="flex items-center gap-2 text-xs text-muted-foreground">
{bookmark?.dueDate && (
<span className="flex items-center gap-1">
<CalendarIcon className="h-3 w-3" />
{new Date(bookmark.dueDate).toLocaleDateString('en-US', { month: 'short', day: 'numeric', timeZone: 'UTC' })}
</span>
)}
{bookmark?.alertAt && (
<span className="flex items-center gap-1">
<Clock className="h-3 w-3" />
Alert set
</span>
)}
</div>
)}
</div>
</div>
)
}