first commit

This commit is contained in:
Lars Behrends
2026-04-09 10:29:11 +02:00
commit dda118a2f7
36 changed files with 14470 additions and 0 deletions

View File

@@ -0,0 +1,101 @@
import { Media } from '@/types';
import { cn } from '@/lib/utils';
import { motion } from 'motion/react';
import { Star, Play, Bookmark } from 'lucide-react';
import { Button } from '@/components/ui/button';
interface MediaListItemProps {
key?: string;
media: Media;
onClick: (media: Media) => void;
}
export default function MediaListItem({ media, onClick }: MediaListItemProps) {
const statusColors = {
watching: 'bg-blue-500',
completed: 'bg-green-500',
planned: 'bg-gray-500',
dropped: 'bg-red-500',
reading: 'bg-amber-500',
listening: 'bg-purple-500',
playing: 'bg-indigo-500',
'on-hold': 'bg-orange-500',
};
const getAspectRatio = () => {
if (media.aspectRatio) return media.aspectRatio;
switch (media.category) {
case 'Music': return '1/1';
case 'Games':
case 'Adult': return '16/9';
default: return '2/3';
}
};
const aspectRatioClass = {
'2/3': 'w-24 h-32',
'16/9': 'w-48 h-27', // 16:9 ratio for w-48 is approx h-27
'1/1': 'w-24 h-24',
}[getAspectRatio()];
return (
<motion.div
layout
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
className="group flex items-center gap-6 p-4 rounded-xl hover:bg-zinc-50 transition-colors cursor-pointer border border-transparent hover:border-zinc-200"
onClick={() => onClick(media)}
>
<div className={cn(
"relative rounded-lg overflow-hidden shrink-0 shadow-md bg-zinc-800 transition-all duration-300",
aspectRatioClass
)}>
<img
src={media.poster}
alt={media.title}
className="w-full h-full object-cover"
referrerPolicy="no-referrer"
/>
{media.status && (
<div className={cn(
"absolute top-2 left-2 w-3 h-3 rounded-full border border-white/20 shadow-sm",
statusColors[media.status]
)} />
)}
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-3 mb-1">
<h3 className="text-lg font-black text-zinc-900 truncate group-hover:text-[#6d28d9] transition-colors">
{media.title}
</h3>
<span className="text-sm font-bold text-zinc-400">({media.year})</span>
</div>
<div className="flex items-center gap-4 mb-3">
<div className="flex items-center gap-1 text-xs font-bold text-zinc-500">
<Star size={14} className="text-yellow-500" fill="currentColor" />
{media.rating || 'N/A'}
</div>
<div className="text-xs font-bold text-zinc-400 uppercase tracking-wider">
{media.genres?.slice(0, 3).join(' • ') || 'Anime'}
</div>
</div>
<p className="text-sm text-zinc-500 line-clamp-2 max-w-2xl">
{media.description || "No description available for this title."}
</p>
</div>
<div className="hidden md:flex items-center gap-2">
<Button size="icon" variant="ghost" className="rounded-full text-zinc-400 hover:text-[#6d28d9] hover:bg-[#6d28d9]/10">
<Play size={18} fill="currentColor" />
</Button>
<Button size="icon" variant="ghost" className="rounded-full text-zinc-400 hover:text-[#6d28d9] hover:bg-[#6d28d9]/10">
<Bookmark size={18} />
</Button>
</div>
</motion.div>
);
}