A personal media tracker built on the AT Protocol opnshelf.xyz
at main 80 lines 2.0 kB view raw
1import type { ListSummaryDto } from "@opnshelf/api"; 2import { Link } from "@tanstack/react-router"; 3import { List, Star } from "lucide-react"; 4import { useTheme } from "@/components/theme-provider"; 5import { 6 M3Card, 7 M3CardContent, 8 M3CardDescription, 9 M3CardHeader, 10 M3CardTitle, 11} from "@/components/ui/m3-card"; 12import { getProfileListDetailRoute } from "@/lib/profile-routes"; 13 14interface ListCardProps { 15 handle: string; 16 list: ListSummaryDto; 17} 18 19export function ListCard({ handle, list }: ListCardProps) { 20 const { seedColor } = useTheme(); 21 22 const getIcon = () => { 23 if (list.slug.includes("watchlist")) { 24 return <List className="w-5 h-5" />; 25 } 26 if (list.slug.includes("favorites")) { 27 return <Star className="w-5 h-5" />; 28 } 29 return <List className="w-5 h-5" />; 30 }; 31 32 return ( 33 <Link 34 {...getProfileListDetailRoute(handle, list.slug)} 35 search={{ page: 1 }} 36 > 37 <M3Card 38 variant="elevated" 39 className="cursor-pointer h-full transition-all hover:md-elevation-2" 40 > 41 <M3CardHeader className="pb-2"> 42 <div className="flex items-center gap-2"> 43 <div 44 className="p-2 rounded-lg" 45 style={{ 46 backgroundColor: `${seedColor}20`, 47 color: seedColor, 48 }} 49 > 50 {getIcon()} 51 </div> 52 <div className="flex-1 min-w-0"> 53 <M3CardTitle className="md-title-medium truncate"> 54 {list.name} 55 </M3CardTitle> 56 {list.isDefault && ( 57 <span className="md-label-small" style={{ color: seedColor }}> 58 Default list 59 </span> 60 )} 61 </div> 62 </div> 63 </M3CardHeader> 64 <M3CardContent> 65 {list.description && ( 66 <M3CardDescription className="line-clamp-2 mb-2"> 67 {list.description} 68 </M3CardDescription> 69 )} 70 <p 71 className="md-body-medium" 72 style={{ color: "var(--md-sys-color-on-surface-variant)" }} 73 > 74 {list.itemCount} item{list.itemCount !== 1 ? "s" : ""} 75 </p> 76 </M3CardContent> 77 </M3Card> 78 </Link> 79 ); 80}