Refactor MovieCard component to introduce a new "minimal" layout option: update layout prop to support "minimal" design, modify default layout to "minimal", and enhance rendering logic for improved user experience across movie sections.

This commit is contained in:
Norbert Maciaszek 2025-08-16 13:50:46 +02:00
parent f88b7ede7d
commit 3865de1c56
2 changed files with 115 additions and 6 deletions

View File

@ -45,7 +45,7 @@ export default async function Home() {
<h2 className="text-3xl font-bold text-white mb-8">Teraz w kinach</h2>
<Carousel>
{nowPlayingMovies.map((movie) => (
<MovieCard key={movie.id} {...movie} layout="zeus" simpleToggle />
<MovieCard key={movie.id} {...movie} simpleToggle />
))}
</Carousel>
</section>
@ -56,7 +56,7 @@ export default async function Home() {
</h2>
<Carousel>
{upcomingMovies.map((movie) => (
<MovieCard key={movie.id} {...movie} layout="zeus" simpleToggle />
<MovieCard key={movie.id} {...movie} simpleToggle />
))}
</Carousel>
</section>
@ -67,7 +67,7 @@ export default async function Home() {
</h2>
<Carousel>
{popularMovies.map((movie) => (
<MovieCard key={movie.id} {...movie} layout="zeus" simpleToggle />
<MovieCard key={movie.id} {...movie} simpleToggle />
))}
</Carousel>
</section>
@ -76,7 +76,7 @@ export default async function Home() {
<h2 className="text-3xl font-bold text-white mb-8">Trendy dnia</h2>
<Carousel>
{trendingMovies.map((movie) => (
<MovieCard key={movie.id} {...movie} layout="zeus" simpleToggle />
<MovieCard key={movie.id} {...movie} simpleToggle />
))}
</Carousel>
</section>

View File

@ -11,7 +11,7 @@ import { FaFire } from "react-icons/fa";
import { RiCalendarCheckLine, RiCalendarScheduleLine } from "react-icons/ri";
type Props = Movie & {
layout?: "default" | "zeus";
layout?: "default" | "zeus" | "minimal";
showDayCounter?: boolean;
simpleToggle?: boolean;
};
@ -20,7 +20,7 @@ const buttonClass =
"p-4 text-sm transition-colors cursor-pointer text-center group/toggle";
export const MovieCard: FC<Props> = ({
layout = "default",
layout = "minimal",
showDayCounter = true,
simpleToggle = false,
...movie
@ -75,6 +75,115 @@ export const MovieCard: FC<Props> = ({
)
);
// Minimal modern theme.
if (layout === "minimal") {
return (
<article className="group relative w-full h-[420px] bg-white/5 border border-white/10 rounded-xl overflow-hidden backdrop-blur-sm transition-all duration-300 hover:bg-white/10 hover:border-white/20 hover:shadow-lg hover:shadow-black/20">
<figure className="relative h-[280px] overflow-hidden">
<img
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105"
src={`http://image.tmdb.org/t/p/w342${poster_path}`}
alt={title}
/>
{/* Rating badge */}
{!!vote_average && (
<div className="absolute top-3 right-3 bg-black/60 backdrop-blur-sm px-2 pr-3 pb-1 rounded-full">
<span className="text-xs font-medium text-yellow-400">
{vote_average.toFixed(1)}
</span>
</div>
)}
{/* Action overlay */}
<div className="absolute inset-0 bg-black/70 backdrop-blur-sm opacity-0 group-hover:opacity-100 transition-all duration-300 flex items-center justify-center">
{!alreadyInStore ? (
<button
onClick={handleAdd}
className="bg-white text-black px-4 py-2 rounded-lg font-medium text-sm transition-all duration-200 hover:bg-gray-100 hover:scale-105"
>
Add to List
</button>
) : (
<div className="flex gap-2">
{isReleased && (
<button
onClick={handleSeen}
className={`p-2 rounded-lg transition-all duration-200 hover:scale-110 ${
seen
? "bg-green-500 text-white"
: "bg-white/20 text-white hover:bg-white/30"
}`}
>
{seen ? <RxEyeOpen size={20} /> : <RxEyeClosed size={20} />}
</button>
)}
<button
onClick={handleFavorite}
className={`p-2 rounded-lg transition-all duration-200 hover:scale-110 ${
favorite
? "bg-red-500 text-white"
: "bg-white/20 text-white hover:bg-white/30"
}`}
>
{favorite ? (
<MdFavorite size={20} />
) : (
<MdFavoriteBorder size={20} />
)}
</button>
<button
onClick={handleRemove}
className="p-2 rounded-lg bg-white/20 text-white hover:bg-red-500 transition-all duration-200 hover:scale-110"
>
<IoMdRemoveCircleOutline size={20} />
</button>
</div>
)}
</div>
</figure>
{/* Content section */}
<div className="p-4 flex flex-col justify-between">
<div>
<h3 className="font-semibold text-lg leading-tight line-clamp-2 mb-2 transition-colors duration-200 group-hover:text-white/90">
{title}
</h3>
<div className="text-sm text-gray-400 leading-relaxed">
<ReadMore text={overview} />
</div>
</div>
<div className="flex items-center justify-between mt-3 pt-3 border-t border-white/10">
<span className="text-xs text-gray-500 font-medium">
{releaseDate.toLocaleDateString("pl-PL", {
day: "numeric",
month: "long",
year: "numeric",
})}
</span>
{alreadyInStore && (
<div className="flex gap-1">
{seen && (
<div
className="w-2 h-2 bg-green-400 rounded-full"
title="Watched"
/>
)}
{favorite && (
<div
className="w-2 h-2 bg-red-400 rounded-full"
title="Favorite"
/>
)}
</div>
)}
</div>
</div>
</article>
);
}
if (layout === "zeus") {
return (
<article className="flex flex-col w-full shadow-lg rounded-t-lg overflow-hidden bg-black/50 shadow-white/5">