A self hosted solution for privately rating and reviewing different sorts of media
at master 124 lines 4.5 kB view raw
1import { ExtendedUserEntry } from '@/app/(app)/dashboard/state'; 2import { cn } from '@/lib/utils'; 3import { Category } from '@prisma/client'; 4import { Book, Film, Star, Tv } from 'lucide-react'; 5import { HTMLProps, ReactNode } from 'react'; 6import SmallRating from './smallRating'; 7import { getUserTitleFromEntry } from '@/server/api/routers/dashboard_'; 8import { Badge } from './ui/badge'; 9 10const UserEntryCard = ({ 11 entryTitle, 12 backgroundImage, 13 category, 14 releaseDate, 15 rating, 16 className, 17 customStars, 18 hoverCard, 19 topRight, 20 ...props 21}: { 22 entryTitle: ReactNode; 23 backgroundImage: string; 24 category: Category; 25 releaseDate: Date; 26 rating: number; 27 customStars?: ReactNode; 28 hoverCard?: ReactNode; 29 topRight?: ReactNode; 30} & HTMLProps<HTMLDivElement>) => { 31 // return <img src={backgroundImage} className="aspect-[2/3] w-full"></img>; 32 33 return ( 34 <div 35 className={cn( 36 'group relative z-50 aspect-[2/3] cursor-pointer overflow-clip rounded-lg bg-cover shadow-sm shadow-base-400 transition-all duration-200 hover:-translate-y-[4px] hover:scale-[101%] hover:shadow-md active:brightness-[0.8]', 37 className 38 )} 39 {...props} 40 > 41 <img src={backgroundImage} className="absolute top-0 h-full w-full" /> 42 <div className="absolute top-0 flex h-[20%] w-full flex-col justify-end rounded-bl-lg rounded-br-lg bg-opacity-50 bg-gradient-to-b from-base-900 to-transparent"></div> 43 <div className="absolute top-0 p-2"> 44 {(() => { 45 switch (category) { 46 case 'Book': 47 return ( 48 <Badge className="flex gap-1 border border-amber-300 bg-amber-700/50 px-1 text-xs text-amber-300"> 49 <Book className="size-3 stroke-amber-300" /> 50 Book 51 </Badge> 52 ); 53 case 'Movie': 54 return ( 55 <Badge className="flex gap-1 border border-blue-300 bg-blue-700/50 px-1 text-xs text-blue-300"> 56 <Book className="size-3 stroke-blue-300" /> 57 Movie 58 </Badge> 59 ); 60 case 'Series': 61 return ( 62 <Badge className="flex gap-1 border border-green-300 bg-green-700/50 px-1 text-xs text-green-300"> 63 <Book className="size-3 stroke-green-300" /> 64 Tv 65 </Badge> 66 ); 67 } 68 })()} 69 </div> 70 {hoverCard && ( 71 <div className="duration-400 absolute right-1 top-1 z-10 -translate-y-4 rounded-sm border border-base-100 bg-white opacity-0 shadow-sm transition-all hover:bg-base-50 hover:text-base-900 group-hover:translate-y-0 group-hover:opacity-100"> 72 {hoverCard} 73 </div> 74 )} 75 <div className="absolute right-0 top-0 flex p-2">{topRight}</div> 76 77 <div className="select-none text-transparent">{entryTitle}</div> 78 <div className="absolute top-[40%] hidden h-[60%] w-full flex-col justify-end rounded-bl-lg rounded-br-lg bg-gradient-to-t from-base-900 to-transparent object-cover p-2 opacity-0 transition-all duration-200 group-hover:opacity-100 md:flex"> 79 <div className="text-left text-sm font-semibold text-white sm:text-base"> 80 {entryTitle} 81 </div> 82 <div className="flex flex-row items-center justify-between"> 83 <div className="text-sm text-base-400"> 84 {isNaN(releaseDate.getFullYear()) ? '' : releaseDate.getFullYear()} 85 </div> 86 {!!customStars && customStars} 87 {!customStars && ( 88 <> 89 <div className="hidden text-white sm:block"> 90 <SmallRating rating={rating} /> 91 </div> 92 <div className="flex items-center gap-1 text-white sm:hidden"> 93 <span className="text-sm">{(rating / 20).toFixed(1)}</span> 94 <Star strokeWidth={0} className="size-4 fill-primary" /> 95 </div> 96 </> 97 )} 98 </div> 99 </div> 100 </div> 101 ); 102}; 103 104export const UserEntryCardObject = ({ 105 userEntry, 106 ...props 107}: { 108 userEntry: ExtendedUserEntry; 109} & HTMLProps<HTMLDivElement>) => { 110 return ( 111 <UserEntryCard 112 {...{ 113 entryTitle: getUserTitleFromEntry(userEntry.entry), 114 backgroundImage: userEntry.entry.posterPath, 115 releaseDate: userEntry.entry.releaseDate, 116 category: userEntry.entry.category, 117 rating: userEntry.rating, 118 }} 119 {...props} 120 /> 121 ); 122}; 123 124export default UserEntryCard;