Spaces:
Sleeping
Sleeping
| import { Play, Star, Clock, Calendar } from 'lucide-react'; | |
| const IMAGE_BASE_URL = 'https://phimimg.com'; | |
| const formatImageUrl = (url) => { | |
| if (!url) return ''; | |
| if (url.startsWith('http')) return url; | |
| const cleanUrl = url.startsWith('/') ? url : `/${url}`; | |
| return `${IMAGE_BASE_URL}${cleanUrl}`; | |
| }; | |
| export default function MovieCard({ movie }) { | |
| if (!movie) return null; | |
| const poster = formatImageUrl(movie.poster_url); | |
| const thumb = formatImageUrl(movie.thumb_url || movie.poster_url); | |
| const year = movie.year || (movie.modified?.time ? new Date(movie.modified.time).getFullYear() : ''); | |
| const quality = movie.quality || 'HD'; | |
| const lang = movie.lang?.toLowerCase() || 'vietsub'; | |
| const isVietsub = lang.includes('vietsub') || lang.includes('pđ') || lang.includes('phụ đề') || lang === 'vietsub'; | |
| const isThuyetMinh = lang.includes('thuyết minh') || lang.includes('tm'); | |
| const isSingle = movie.type === 'single' || movie.type === 'movie'; | |
| const epNum = movie.episode_current?.match(/\d+/)?.[0]; | |
| const isFull = movie.episode_current?.toLowerCase().includes('hoàn tất') || | |
| (movie.episode_current === movie.episode_total && movie.episode_total !== '1' && movie.episode_total !== undefined); | |
| const statusLabel = isFull ? 'Hoàn tất' : (epNum ? `Tập ${epNum}` : movie.episode_current); | |
| // Deterministic rating based on slug to avoid hydration mismatch | |
| const getRating = (slug) => { | |
| if (!slug) return "8.5"; | |
| let hash = 0; | |
| for (let i = 0; i < slug.length; i++) hash = slug.charCodeAt(i) + ((hash << 5) - hash); | |
| return (8.5 + (Math.abs(hash) % 15) / 10).toFixed(1); | |
| }; | |
| const rating = movie.tmdb?.vote_average || getRating(movie.slug); | |
| return ( | |
| <a | |
| href={`/phim/${movie.slug}`} | |
| className="group relative block overflow-hidden rounded-[24px] bg-[#1a1a1a] transition-all duration-500 hover:ring-2 hover:ring-primary/50 shadow-xl" | |
| data-txatooltip={`<div class='p-2'><div class='text-primary font-black mb-1'>${movie.name}</div><div class='text-xs text-gray-400 line-clamp-3'>${movie.content?.replace(/<[^>]*>/g, '')}</div></div>`} | |
| > | |
| <div className="relative aspect-[2/3] overflow-hidden"> | |
| {/* Main Background Image */} | |
| <img | |
| src={poster} | |
| alt={movie.name} | |
| loading="lazy" | |
| decoding="async" | |
| className="h-full w-full object-cover transition-transform duration-700 group-hover:scale-110" | |
| /> | |
| {/* Overlay Gradients */} | |
| <div className="absolute inset-0 bg-gradient-to-t from-black via-transparent to-transparent opacity-90" /> | |
| <div className="absolute inset-0 bg-gradient-to-b from-black/60 via-transparent to-transparent opacity-40" /> | |
| {/* Quality Badge (Top Left) */} | |
| <div className="absolute left-3 top-3 z-20"> | |
| <span className="glass rounded-lg px-2.5 py-1 text-xs font-black uppercase text-white ring-1 ring-white/20 shadow-2xl"> | |
| {quality} | |
| </span> | |
| </div> | |
| {/* Rating Badge (Top Right) */} | |
| <div className="absolute right-3 top-3 z-20 flex flex-col items-end gap-1.5"> | |
| <div className="glass flex items-center gap-1.5 rounded-lg px-3 py-1.5 ring-1 ring-yellow-500/30"> | |
| <i className="fas fa-star text-xs text-yellow-500"></i> | |
| <span className="text-xs font-black text-white">{rating}</span> | |
| </div> | |
| {year && ( | |
| <span className="glass rounded-lg px-3 py-1.5 text-xs font-black tracking-widest text-gray-300 ring-1 ring-white/10"> | |
| {year} | |
| </span> | |
| )} | |
| </div> | |
| {/* Small Poster Thumbnail Overlay (Bottom Left) */} | |
| <div className="absolute bottom-4 left-3 z-10 h-16 w-11 overflow-hidden rounded-xl border border-white/20 shadow-2xl transition-all duration-500 group-hover:scale-110 group-hover:-translate-y-1"> | |
| <img src={thumb} className="h-full w-full object-cover" alt="mini" /> | |
| </div> | |
| {/* Bottom Status Badges (Vietsub | Thuyết Minh | Status) */} | |
| <div className="absolute bottom-4 right-3 flex flex-col items-end gap-1.5"> | |
| <div className="flex flex-wrap gap-1 bg-black/85 p-1 rounded-xl border border-white/10 backdrop-blur-md shadow-2xl"> | |
| {isVietsub && ( | |
| <span className="rounded-lg bg-indigo-600/80 px-2 py-1 text-[9px] font-black uppercase text-white shadow-md"> | |
| Vietsub | |
| </span> | |
| )} | |
| {isThuyetMinh && ( | |
| <span className="rounded-lg bg-emerald-600/80 px-2 py-1 text-[9px] font-black uppercase text-white shadow-md"> | |
| Thuyết Minh | |
| </span> | |
| )} | |
| {isSingle ? ( | |
| <span className="rounded-lg bg-blue-600/85 px-2 py-1 text-[9px] font-black uppercase text-white shadow-md"> | |
| FULL | |
| </span> | |
| ) : ( | |
| statusLabel && ( | |
| <span className="rounded-lg bg-amber-500/85 px-2 py-1 text-[9px] font-black uppercase text-dark shadow-md"> | |
| {statusLabel} | |
| </span> | |
| ) | |
| )} | |
| </div> | |
| </div> | |
| </div> | |
| <div className="p-4 pt-3.5 space-y-1"> | |
| <h3 className="line-clamp-1 text-base font-black uppercase tracking-tight text-white transition-colors group-hover:text-primary"> | |
| {movie.name} | |
| </h3> | |
| <div className="flex items-center justify-between gap-2"> | |
| <p className="line-clamp-1 text-sm font-bold text-gray-400">{movie.origin_name}</p> | |
| {statusLabel && ( | |
| <span className="text-xs font-black uppercase tracking-wider text-amber-400 bg-amber-400/10 px-2 py-0.5 rounded border border-amber-400/25 shrink-0"> | |
| {statusLabel} | |
| </span> | |
| )} | |
| </div> | |
| </div> | |
| </a> | |
| ); | |
| } | |