Refactor MovieCard component to support multiple layouts: introduce Aurora, Minimal, and Zeus layouts, enhancing visual variety and user interaction. Update rendering logic to accommodate new layout options and streamline component structure for improved maintainability.
This commit is contained in:
parent
1e7d7891d0
commit
33b8373c36
|
|
@ -1,14 +1,14 @@
|
|||
"use client";
|
||||
import { FC, useState } from "react";
|
||||
import { ReadMore } from "../ReadMore";
|
||||
import { FC } from "react";
|
||||
import { addMovie, deleteMovie, updateMovie } from "@/lib/db";
|
||||
import { useGlobalStore } from "@/app/store/globalStore";
|
||||
import { MdFavorite, MdFavoriteBorder, MdOutlinePostAdd } from "react-icons/md";
|
||||
import { RxEyeClosed, RxEyeOpen } from "react-icons/rx";
|
||||
import { IoMdRemoveCircleOutline } from "react-icons/io";
|
||||
import { Movie } from "@/types/global";
|
||||
import { FaAngleUp, FaArrowUp, FaFire, FaTrash } from "react-icons/fa";
|
||||
import { RiCalendarCheckLine, RiCalendarScheduleLine } from "react-icons/ri";
|
||||
import {
|
||||
AuroraLayout,
|
||||
MinimalLayout,
|
||||
ZeusLayout,
|
||||
DefaultLayout,
|
||||
} from "./layouts";
|
||||
|
||||
type Props = Movie & {
|
||||
layout?: "default" | "zeus" | "minimal" | "aurora";
|
||||
|
|
@ -16,38 +16,26 @@ type Props = Movie & {
|
|||
simpleToggle?: boolean;
|
||||
};
|
||||
|
||||
const buttonClass =
|
||||
"p-4 text-sm transition-colors cursor-pointer text-center group/toggle";
|
||||
|
||||
export const MovieCard: FC<Props> = ({
|
||||
layout = "minimal",
|
||||
showDayCounter = true,
|
||||
simpleToggle = false,
|
||||
...movie
|
||||
}) => {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const {
|
||||
movies,
|
||||
addMovie: addMovieToStore,
|
||||
deleteMovie: deleteMovieFromStore,
|
||||
updateMovie: updateMovieInStore,
|
||||
} = useGlobalStore();
|
||||
const {
|
||||
id,
|
||||
title,
|
||||
overview,
|
||||
popularity,
|
||||
release_date,
|
||||
poster_path,
|
||||
vote_average,
|
||||
} = movie;
|
||||
|
||||
const { id } = movie;
|
||||
const alreadyInStore = movies.find((m) => m.id === id);
|
||||
|
||||
const isReleased = new Date(release_date) < new Date();
|
||||
const isReleased = new Date(movie.release_date) < new Date();
|
||||
const iconSize = 48;
|
||||
|
||||
const seen = movie.seen;
|
||||
const favorite = movie.favorite;
|
||||
const buttonClass =
|
||||
"p-4 text-sm transition-colors cursor-pointer text-center group/toggle";
|
||||
|
||||
const handleAdd = async () => {
|
||||
await addMovie(movie);
|
||||
|
|
@ -60,519 +48,46 @@ export const MovieCard: FC<Props> = ({
|
|||
};
|
||||
|
||||
const handleSeen = async () => {
|
||||
await updateMovie(id, { seen: !seen });
|
||||
updateMovieInStore(id, { seen: !seen });
|
||||
await updateMovie(id, { seen: !movie.seen });
|
||||
updateMovieInStore(id, { seen: !movie.seen });
|
||||
};
|
||||
|
||||
const handleFavorite = async () => {
|
||||
await updateMovie(id, { favorite: !favorite });
|
||||
updateMovieInStore(id, { favorite: !favorite });
|
||||
await updateMovie(id, { favorite: !movie.favorite });
|
||||
updateMovieInStore(id, { favorite: !movie.favorite });
|
||||
};
|
||||
|
||||
const releaseDate = new Date(release_date);
|
||||
const releaseDate = new Date(movie.release_date);
|
||||
const daysSinceRelease = Math.abs(
|
||||
Math.floor(
|
||||
(new Date().getTime() - releaseDate.getTime()) / (1000 * 60 * 60 * 24)
|
||||
)
|
||||
);
|
||||
|
||||
// Aurora theme - creative and visually stunning.
|
||||
if (layout === "aurora") {
|
||||
const scoreColor =
|
||||
vote_average >= 8
|
||||
? "from-emerald-400 to-teal-400"
|
||||
: vote_average >= 6
|
||||
? "from-yellow-400 to-orange-400"
|
||||
: "from-red-400 to-pink-400";
|
||||
|
||||
return (
|
||||
<article
|
||||
className="group relative w-full overflow-hidden rounded-2xl"
|
||||
onMouseLeave={() => setIsExpanded(false)}
|
||||
>
|
||||
{/* Aurora background effect */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-purple-900/20 via-blue-900/20 to-teal-900/20 opacity-60"></div>
|
||||
<div className="absolute inset-0 bg-gradient-to-tr from-pink-500/10 via-transparent to-cyan-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-1000"></div>
|
||||
|
||||
{/* Main card container */}
|
||||
<div className="grid relative h-full bg-gradient-to-br from-slate-800/90 to-slate-900/90 backdrop-blur-xl border border-slate-700/50 shadow-2xl shadow-purple-500/10 group-hover:shadow-purple-500/20 transition-all duration-500">
|
||||
{/* Image section with sophisticated overlay */}
|
||||
<figure
|
||||
className="relative overflow-hidden"
|
||||
style={{
|
||||
aspectRatio: "342/513",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
className="w-full h-full object-cover transition-all duration-700 group-hover:scale-110 group-hover:brightness-110"
|
||||
src={`http://image.tmdb.org/t/p/w342${poster_path}`}
|
||||
alt={title}
|
||||
/>
|
||||
|
||||
{/* Gradient overlays for depth */}
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-slate-900 via-slate-900/20 to-transparent"></div>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-purple-600/20 via-transparent to-cyan-600/20 opacity-0 group-hover:opacity-100 transition-opacity duration-700"></div>
|
||||
|
||||
{/* Floating rating badge */}
|
||||
{!!vote_average && (
|
||||
<div className="absolute top-4 right-4 transform rotate-3 group-hover:rotate-0 transition-transform duration-300">
|
||||
<div
|
||||
className={`bg-gradient-to-r ${scoreColor} p-3 rounded-xl shadow-lg backdrop-blur-sm`}
|
||||
>
|
||||
<div className="flex items-center gap-2 text-white font-bold">
|
||||
<span className="text-xl">★</span>
|
||||
<span className="text-lg">{vote_average.toFixed(1)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Popularity indicator */}
|
||||
<div className="absolute top-4 left-4 bg-black/60 backdrop-blur-sm px-3 py-2 rounded-xl border border-white/20">
|
||||
<div className="flex items-center gap-2 text-orange-400">
|
||||
<FaFire className="animate-pulse" />
|
||||
<span className="text-sm font-medium">
|
||||
{Math.round(popularity)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* x Days left to release */}
|
||||
{(!isReleased || daysSinceRelease < 35) && (
|
||||
<div className="absolute bottom-4 right-4 flex justify-center">
|
||||
<div className="text-white bg-black/60 backdrop-blur-sm px-3 py-2 rounded-xl border border-white/20">
|
||||
<span className="text-xs font-medium">
|
||||
{isReleased &&
|
||||
daysSinceRelease < 35 &&
|
||||
`od ${daysSinceRelease} dni`}
|
||||
{!isReleased && `za ${daysSinceRelease} dni`}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Status indicators */}
|
||||
{alreadyInStore && (
|
||||
<div className="absolute bottom-4 left-4 flex gap-2">
|
||||
<div
|
||||
className={`${
|
||||
seen ? "bg-emerald-500/90" : "bg-white/20"
|
||||
} backdrop-blur-sm p-2 rounded-full animate-pulse cursor-pointer`}
|
||||
onClick={handleSeen}
|
||||
>
|
||||
<RxEyeOpen size={16} className="text-white" />
|
||||
</div>
|
||||
<div
|
||||
className={`${
|
||||
favorite ? "bg-rose-500/90" : "bg-white/20"
|
||||
} backdrop-blur-sm p-2 rounded-full animate-pulse cursor-pointer`}
|
||||
onClick={handleFavorite}
|
||||
>
|
||||
<MdFavorite size={16} className="text-white" />
|
||||
</div>
|
||||
<div
|
||||
className={`bg-white/20 backdrop-blur-sm p-2 rounded-full animate-pulse cursor-pointer`}
|
||||
onClick={handleRemove}
|
||||
>
|
||||
<FaTrash size={16} className="text-white" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Magical action overlay */}
|
||||
{!alreadyInStore && (
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-purple-900/40 to-transparent opacity-0 group-hover:opacity-100 transition-all duration-500 flex items-center justify-center">
|
||||
<button
|
||||
onClick={handleAdd}
|
||||
className="relative overflow-hidden bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-500 hover:to-pink-500 text-white px-8 py-4 rounded-2xl font-semibold text-lg shadow-2xl transform hover:scale-105 transition-all duration-300 group/btn"
|
||||
>
|
||||
<span className="relative z-10">Dodaj do listy</span>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-white/20 to-transparent opacity-0 group-hover/btn:opacity-100 transition-opacity duration-300"></div>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</figure>
|
||||
|
||||
{/* Content section with glowing effects */}
|
||||
<div className="relative p-6 flex flex-col justify-between">
|
||||
{/* Subtle glow effect */}
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-purple-500/5 to-transparent pointer-events-none"></div>
|
||||
|
||||
<div className="relative z-10">
|
||||
<h3 className="font-bold text-xl leading-tight mb-3 text-transparent bg-clip-text bg-gradient-to-r from-white to-gray-300 group-hover:from-purple-200 group-hover:to-cyan-200 transition-all duration-500">
|
||||
{title}
|
||||
</h3>
|
||||
<p
|
||||
className="text-sm text-gray-400 line-clamp-2 leading-relaxed opacity-80 group-hover:opacity-100 transition-opacity duration-300 hover:text-secondary cursor-pointer"
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
>
|
||||
{overview}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Bottom section with enhanced styling */}
|
||||
<div className="relative z-10 flex items-center justify-between pt-4 mt-4 border-t border-gradient-to-r border-slate-700/50">
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
className={`flex items-center gap-1 text-sm ${
|
||||
isReleased ? "text-emerald-400" : "text-amber-400"
|
||||
}`}
|
||||
>
|
||||
{isReleased ? (
|
||||
<RiCalendarCheckLine />
|
||||
) : (
|
||||
<RiCalendarScheduleLine />
|
||||
)}
|
||||
<span className="font-medium">
|
||||
{releaseDate.toLocaleDateString("pl-PL", {
|
||||
day: "numeric",
|
||||
month: "short",
|
||||
year: "numeric",
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{alreadyInStore && (
|
||||
<div className="flex gap-2">
|
||||
{seen && (
|
||||
<div
|
||||
className="w-3 h-3 bg-gradient-to-r from-emerald-400 to-teal-400 rounded-full shadow-lg shadow-emerald-400/50 animate-pulse"
|
||||
title="Watched"
|
||||
/>
|
||||
)}
|
||||
{favorite && (
|
||||
<div
|
||||
className="w-3 h-3 bg-gradient-to-r from-rose-400 to-pink-400 rounded-full shadow-lg shadow-rose-400/50 animate-pulse"
|
||||
title="Favorite"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Decorative border glow */}
|
||||
<div className="absolute inset-0 rounded-2xl border border-transparent bg-gradient-to-r from-purple-500/20 via-transparent to-cyan-500/20 opacity-0 group-hover:opacity-100 transition-opacity duration-700 pointer-events-none"></div>
|
||||
</div>
|
||||
|
||||
{isExpanded && (
|
||||
<div
|
||||
className="absolute inset-0 p-4 bg-black bg-gradient-to-t from-purple-500/30 to-secondary/30 cursor-pointer"
|
||||
onClick={() => setIsExpanded(false)}
|
||||
>
|
||||
<p>{overview}</p>
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
// 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">
|
||||
<figure className="relative ">
|
||||
<img
|
||||
style={{
|
||||
aspectRatio: "342/513",
|
||||
}}
|
||||
className="w-full object-cover"
|
||||
src={`http://image.tmdb.org/t/p/w342${poster_path}`}
|
||||
/>
|
||||
<span className="absolute inset-0 bg-black/30 backdrop-blur-md opacity-0 hover-any:opacity-100 transition-opacity duration-300 flex items-center justify-center cursor-pointer">
|
||||
{!alreadyInStore && (
|
||||
<button className={buttonClass} onClick={handleAdd}>
|
||||
<MdOutlinePostAdd size={64} color="white" />
|
||||
</button>
|
||||
)}
|
||||
{alreadyInStore && (
|
||||
<div className="flex flex-col">
|
||||
<>
|
||||
{isReleased && !simpleToggle && (
|
||||
<button
|
||||
className={`${buttonClass} text-white`}
|
||||
onClick={handleSeen}
|
||||
>
|
||||
<span className="group-hover/toggle:hidden">
|
||||
{seen ? (
|
||||
<RxEyeOpen size={iconSize} />
|
||||
) : (
|
||||
<RxEyeClosed size={iconSize} />
|
||||
)}
|
||||
</span>
|
||||
<span className="hidden group-hover/toggle:block">
|
||||
{seen ? (
|
||||
<RxEyeClosed size={iconSize} />
|
||||
) : (
|
||||
<RxEyeOpen size={iconSize} />
|
||||
)}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
{!simpleToggle && (
|
||||
<button
|
||||
className={`${buttonClass} text-amber-400`}
|
||||
onClick={handleFavorite}
|
||||
>
|
||||
<span className="group-hover/toggle:hidden">
|
||||
{favorite ? (
|
||||
<MdFavorite size={iconSize} />
|
||||
) : (
|
||||
<MdFavoriteBorder size={iconSize} />
|
||||
)}
|
||||
</span>
|
||||
<span className="hidden group-hover/toggle:block">
|
||||
{favorite ? (
|
||||
<MdFavoriteBorder size={iconSize} />
|
||||
) : (
|
||||
<MdFavorite size={iconSize} />
|
||||
)}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
className={`${buttonClass} text-red-500`}
|
||||
onClick={handleRemove}
|
||||
>
|
||||
<IoMdRemoveCircleOutline size={iconSize} />
|
||||
</button>
|
||||
</>
|
||||
</div>
|
||||
)}
|
||||
</span>
|
||||
<span className="absolute top-0 right-0 bg-black/50 px-2 py-1 rounded-bl-lg">
|
||||
<p className="text-sm text-white flex items-center gap-1">
|
||||
<FaFire />
|
||||
{popularity}
|
||||
</p>
|
||||
</span>
|
||||
</figure>
|
||||
<div className="p-4">
|
||||
{!!vote_average && (
|
||||
<p className="flex items-center gap-1 text-sm mb-2">
|
||||
<span className="text-yellow-400">★</span>
|
||||
<span>{vote_average.toFixed(1)}/10</span>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="flex justify-between">
|
||||
<h2 className="text-xl leading-[1.1] font-bold">{title}</h2>
|
||||
</div>
|
||||
|
||||
<p
|
||||
className={`text-sm mt-2 flex items-center gap-1 leading-[1.1] ${
|
||||
isReleased ? "text-green-700" : "text-yellow-500"
|
||||
}`}
|
||||
>
|
||||
{isReleased ? <RiCalendarCheckLine /> : <RiCalendarScheduleLine />}
|
||||
<span>
|
||||
{releaseDate.toLocaleDateString("pl-PL", {
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
})}
|
||||
</span>
|
||||
</p>
|
||||
{showDayCounter && (
|
||||
<span className="text-xs text-gray-400">
|
||||
{isReleased
|
||||
? `${daysSinceRelease} dni od premiery`
|
||||
: `${daysSinceRelease} dni do premiery`}
|
||||
</span>
|
||||
)}
|
||||
<div className="text-xs text-gray-400 mt-4">
|
||||
<ReadMore text={overview} />
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex w-full shadow-md rounded-lg overflow-hidden mx-auto group/card">
|
||||
<div className="overflow-hidden rounded-xl relative movie-item text-white movie-card">
|
||||
<div className="absolute inset-0 z-10 bg-gradient-to-t from-black via-gray-900 to-transparent"></div>
|
||||
<div className="relative group z-10 p-6 space-y-6 h-full">
|
||||
<div className="align-self-end w-full h-full flex flex-col">
|
||||
<div className="h-64"></div>
|
||||
<div className="flex flex-col space-y-2 inner mb-4">
|
||||
<h3
|
||||
className="text-lg leading-[1.3] font-bold text-white line-clamp-1"
|
||||
title={title}
|
||||
>
|
||||
{title}
|
||||
</h3>
|
||||
<div className="text-xs text-gray-400">
|
||||
<ReadMore text={overview} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row justify-between mt-auto">
|
||||
<div className="flex flex-col">
|
||||
<div className="text-sm text-gray-400">Popularity:</div>
|
||||
<div className="popularity">{popularity}</div>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<div className="text-sm text-gray-400">Release date:</div>
|
||||
<div className="release">{release_date}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* <div className="absolute top-0 z-10 bg-transparent inset-x-0 group-hover/card:bg-white/50 transition-all opacity-0 group-hover/card:opacity-100 flex flex-col justify-center"> */}
|
||||
<div className="absolute top-0 z-10 bg-transparent inset-0 group-hover/card:bg-black/50 transition-all opacity-0 group-hover/card:opacity-100 flex flex-col justify-center">
|
||||
{!alreadyInStore && (
|
||||
<button
|
||||
className={`${buttonClass} bg-primary/70 text-white hover:bg-primary`}
|
||||
onClick={handleAdd}
|
||||
>
|
||||
Add to list
|
||||
</button>
|
||||
)}
|
||||
{alreadyInStore && (
|
||||
<>
|
||||
{isReleased && (
|
||||
<button
|
||||
className={`${buttonClass} bg-accent/70 text-white hover:bg-accent`}
|
||||
onClick={handleSeen}
|
||||
>
|
||||
{seen ? "Mark as unseen" : "Mark as seen"}
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
className={`${buttonClass} bg-amber-400/70 text-white hover:bg-amber-500`}
|
||||
onClick={handleFavorite}
|
||||
>
|
||||
{favorite ? "Remove favorite" : "Add to favorites"}
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`${buttonClass} bg-primary/70 text-white hover:bg-primary`}
|
||||
onClick={handleRemove}
|
||||
>
|
||||
Remove from list
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<figure className="absolute inset-0 w-full bottom-[20%]">
|
||||
<img
|
||||
className="w-full h-96 object-cover"
|
||||
src={`http://image.tmdb.org/t/p/w342${poster_path}`}
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
const commonProps = {
|
||||
...movie,
|
||||
alreadyInStore,
|
||||
isReleased,
|
||||
handleAdd,
|
||||
handleRemove,
|
||||
handleSeen,
|
||||
handleFavorite,
|
||||
daysSinceRelease,
|
||||
releaseDate,
|
||||
showDayCounter,
|
||||
simpleToggle,
|
||||
buttonClass,
|
||||
iconSize,
|
||||
};
|
||||
|
||||
switch (layout) {
|
||||
case "aurora":
|
||||
return <AuroraLayout {...commonProps} />;
|
||||
case "minimal":
|
||||
return <MinimalLayout {...commonProps} />;
|
||||
case "zeus":
|
||||
return <ZeusLayout {...commonProps} />;
|
||||
default:
|
||||
return <DefaultLayout {...commonProps} />;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,230 @@
|
|||
"use client";
|
||||
import { FC, useState } from "react";
|
||||
import { MdFavorite } from "react-icons/md";
|
||||
import { RxEyeOpen } from "react-icons/rx";
|
||||
import { FaFire, FaTrash } from "react-icons/fa";
|
||||
import { RiCalendarCheckLine, RiCalendarScheduleLine } from "react-icons/ri";
|
||||
import { Movie } from "@/types/global";
|
||||
|
||||
interface AuroraLayoutProps extends Movie {
|
||||
showDayCounter?: boolean;
|
||||
simpleToggle?: boolean;
|
||||
alreadyInStore?: Movie | undefined;
|
||||
isReleased: boolean;
|
||||
handleAdd: () => void;
|
||||
handleRemove: () => void;
|
||||
handleSeen: () => void;
|
||||
handleFavorite: () => void;
|
||||
daysSinceRelease: number;
|
||||
releaseDate: Date;
|
||||
}
|
||||
|
||||
export const AuroraLayout: FC<AuroraLayoutProps> = ({
|
||||
title,
|
||||
overview,
|
||||
popularity,
|
||||
release_date,
|
||||
poster_path,
|
||||
vote_average,
|
||||
seen,
|
||||
favorite,
|
||||
alreadyInStore,
|
||||
isReleased,
|
||||
handleAdd,
|
||||
handleRemove,
|
||||
handleSeen,
|
||||
handleFavorite,
|
||||
daysSinceRelease,
|
||||
releaseDate,
|
||||
}) => {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
const scoreColor =
|
||||
vote_average >= 8
|
||||
? "from-emerald-400 to-teal-400"
|
||||
: vote_average >= 6
|
||||
? "from-yellow-400 to-orange-400"
|
||||
: "from-red-400 to-pink-400";
|
||||
|
||||
return (
|
||||
<article
|
||||
className="group relative w-full overflow-hidden rounded-2xl"
|
||||
onMouseLeave={() => setIsExpanded(false)}
|
||||
>
|
||||
{/* Aurora background effect */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-purple-900/20 via-blue-900/20 to-teal-900/20 opacity-60"></div>
|
||||
<div className="absolute inset-0 bg-gradient-to-tr from-pink-500/10 via-transparent to-cyan-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-1000"></div>
|
||||
|
||||
{/* Main card container */}
|
||||
<div className="grid relative h-full bg-gradient-to-br from-slate-800/90 to-slate-900/90 backdrop-blur-xl border border-slate-700/50 shadow-2xl shadow-purple-500/10 group-hover:shadow-purple-500/20 transition-all duration-500">
|
||||
{/* Image section with sophisticated overlay */}
|
||||
<figure
|
||||
className="relative overflow-hidden"
|
||||
style={{
|
||||
aspectRatio: "342/513",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
className="w-full h-full object-cover transition-all duration-700 group-hover:scale-110 group-hover:brightness-110"
|
||||
src={`http://image.tmdb.org/t/p/w342${poster_path}`}
|
||||
alt={title}
|
||||
/>
|
||||
|
||||
{/* Gradient overlays for depth */}
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-slate-900 via-slate-900/20 to-transparent"></div>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-purple-600/20 via-transparent to-cyan-600/20 opacity-0 group-hover:opacity-100 transition-opacity duration-700"></div>
|
||||
|
||||
{/* Floating rating badge */}
|
||||
{!!vote_average && (
|
||||
<div className="absolute top-4 right-4 transform rotate-3 group-hover:rotate-0 transition-transform duration-300">
|
||||
<div
|
||||
className={`bg-gradient-to-r ${scoreColor} p-3 rounded-xl shadow-lg backdrop-blur-sm`}
|
||||
>
|
||||
<div className="flex items-center gap-2 text-white font-bold">
|
||||
<span className="text-xl">★</span>
|
||||
<span className="text-lg">{vote_average.toFixed(1)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Popularity indicator */}
|
||||
<div className="absolute top-4 left-4 bg-black/60 backdrop-blur-sm px-3 py-2 rounded-xl border border-white/20">
|
||||
<div className="flex items-center gap-2 text-orange-400">
|
||||
<FaFire className="animate-pulse" />
|
||||
<span className="text-sm font-medium">
|
||||
{Math.round(popularity)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Days left to release */}
|
||||
{(!isReleased || daysSinceRelease < 35) && (
|
||||
<div className="absolute bottom-4 right-4 flex justify-center">
|
||||
<div className="text-white bg-black/60 backdrop-blur-sm px-3 py-2 rounded-xl border border-white/20">
|
||||
<span className="text-xs font-medium">
|
||||
{isReleased &&
|
||||
daysSinceRelease < 35 &&
|
||||
`od ${daysSinceRelease} dni`}
|
||||
{!isReleased && `za ${daysSinceRelease} dni`}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Status indicators */}
|
||||
{alreadyInStore && (
|
||||
<div className="absolute bottom-4 left-4 flex gap-2">
|
||||
<div
|
||||
className={`${
|
||||
seen ? "bg-emerald-500/90" : "bg-white/20"
|
||||
} backdrop-blur-sm p-2 rounded-full animate-pulse cursor-pointer`}
|
||||
onClick={handleSeen}
|
||||
>
|
||||
<RxEyeOpen size={16} className="text-white" />
|
||||
</div>
|
||||
<div
|
||||
className={`${
|
||||
favorite ? "bg-rose-500/90" : "bg-white/20"
|
||||
} backdrop-blur-sm p-2 rounded-full animate-pulse cursor-pointer`}
|
||||
onClick={handleFavorite}
|
||||
>
|
||||
<MdFavorite size={16} className="text-white" />
|
||||
</div>
|
||||
<div
|
||||
className={`bg-white/20 backdrop-blur-sm p-2 rounded-full animate-pulse cursor-pointer`}
|
||||
onClick={handleRemove}
|
||||
>
|
||||
<FaTrash size={16} className="text-white" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Magical action overlay */}
|
||||
{!alreadyInStore && (
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-purple-900/40 to-transparent opacity-0 group-hover:opacity-100 transition-all duration-500 flex items-center justify-center">
|
||||
<button
|
||||
onClick={handleAdd}
|
||||
className="relative overflow-hidden bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-500 hover:to-pink-500 text-white px-8 py-4 rounded-2xl font-semibold text-lg shadow-2xl transform hover:scale-105 transition-all duration-300 group/btn"
|
||||
>
|
||||
<span className="relative z-10">Dodaj do listy</span>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-white/20 to-transparent opacity-0 group-hover/btn:opacity-100 transition-opacity duration-300"></div>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</figure>
|
||||
|
||||
{/* Content section with glowing effects */}
|
||||
<div className="relative p-6 flex flex-col justify-between">
|
||||
{/* Subtle glow effect */}
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-purple-500/5 to-transparent pointer-events-none"></div>
|
||||
|
||||
<div className="relative z-10">
|
||||
<h3 className="font-bold text-xl leading-tight mb-3 text-transparent bg-clip-text bg-gradient-to-r from-white to-gray-300 group-hover:from-purple-200 group-hover:to-cyan-200 transition-all duration-500">
|
||||
{title}
|
||||
</h3>
|
||||
<p
|
||||
className="text-sm text-gray-400 line-clamp-2 leading-relaxed opacity-80 group-hover:opacity-100 transition-opacity duration-300 hover:text-secondary cursor-pointer"
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
>
|
||||
{overview}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Bottom section with enhanced styling */}
|
||||
<div className="relative z-10 flex items-center justify-between pt-4 mt-4 border-t border-gradient-to-r border-slate-700/50">
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
className={`flex items-center gap-1 text-sm ${
|
||||
isReleased ? "text-emerald-400" : "text-amber-400"
|
||||
}`}
|
||||
>
|
||||
{isReleased ? (
|
||||
<RiCalendarCheckLine />
|
||||
) : (
|
||||
<RiCalendarScheduleLine />
|
||||
)}
|
||||
<span className="font-medium">
|
||||
{releaseDate.toLocaleDateString("pl-PL", {
|
||||
day: "numeric",
|
||||
month: "short",
|
||||
year: "numeric",
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{alreadyInStore && (
|
||||
<div className="flex gap-2">
|
||||
{seen && (
|
||||
<div
|
||||
className="w-3 h-3 bg-gradient-to-r from-emerald-400 to-teal-400 rounded-full shadow-lg shadow-emerald-400/50 animate-pulse"
|
||||
title="Watched"
|
||||
/>
|
||||
)}
|
||||
{favorite && (
|
||||
<div
|
||||
className="w-3 h-3 bg-gradient-to-r from-rose-400 to-pink-400 rounded-full shadow-lg shadow-rose-400/50 animate-pulse"
|
||||
title="Favorite"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Decorative border glow */}
|
||||
<div className="absolute inset-0 rounded-2xl border border-transparent bg-gradient-to-r from-purple-500/20 via-transparent to-cyan-500/20 opacity-0 group-hover:opacity-100 transition-opacity duration-700 pointer-events-none"></div>
|
||||
</div>
|
||||
|
||||
{isExpanded && (
|
||||
<div
|
||||
className="absolute inset-0 p-4 bg-black bg-gradient-to-t from-purple-500/30 to-secondary/30 cursor-pointer"
|
||||
onClick={() => setIsExpanded(false)}
|
||||
>
|
||||
<p>{overview}</p>
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
"use client";
|
||||
import { FC } from "react";
|
||||
import { ReadMore } from "../../ReadMore";
|
||||
import { Movie } from "@/types/global";
|
||||
|
||||
interface DefaultLayoutProps extends Movie {
|
||||
showDayCounter?: boolean;
|
||||
simpleToggle?: boolean;
|
||||
alreadyInStore?: Movie | undefined;
|
||||
isReleased: boolean;
|
||||
handleAdd: () => void;
|
||||
handleRemove: () => void;
|
||||
handleSeen: () => void;
|
||||
handleFavorite: () => void;
|
||||
buttonClass: string;
|
||||
}
|
||||
|
||||
export const DefaultLayout: FC<DefaultLayoutProps> = ({
|
||||
title,
|
||||
overview,
|
||||
popularity,
|
||||
release_date,
|
||||
poster_path,
|
||||
alreadyInStore,
|
||||
isReleased,
|
||||
handleAdd,
|
||||
handleRemove,
|
||||
handleSeen,
|
||||
handleFavorite,
|
||||
buttonClass,
|
||||
}) => {
|
||||
return (
|
||||
<div className="flex w-full shadow-md rounded-lg overflow-hidden mx-auto group/card">
|
||||
<div className="overflow-hidden rounded-xl relative movie-item text-white movie-card">
|
||||
<div className="absolute inset-0 z-10 bg-gradient-to-t from-black via-gray-900 to-transparent"></div>
|
||||
<div className="relative group z-10 p-6 space-y-6 h-full">
|
||||
<div className="align-self-end w-full h-full flex flex-col">
|
||||
<div className="h-64"></div>
|
||||
<div className="flex flex-col space-y-2 inner mb-4">
|
||||
<h3
|
||||
className="text-lg leading-[1.3] font-bold text-white line-clamp-1"
|
||||
title={title}
|
||||
>
|
||||
{title}
|
||||
</h3>
|
||||
<div className="text-xs text-gray-400">
|
||||
<ReadMore text={overview} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row justify-between mt-auto">
|
||||
<div className="flex flex-col">
|
||||
<div className="text-sm text-gray-400">Popularity:</div>
|
||||
<div className="popularity">{popularity}</div>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<div className="text-sm text-gray-400">Release date:</div>
|
||||
<div className="release">{release_date}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute top-0 z-10 bg-transparent inset-0 group-hover/card:bg-black/50 transition-all opacity-0 group-hover/card:opacity-100 flex flex-col justify-center">
|
||||
{!alreadyInStore && (
|
||||
<button
|
||||
className={`${buttonClass} bg-primary/70 text-white hover:bg-primary`}
|
||||
onClick={handleAdd}
|
||||
>
|
||||
Add to list
|
||||
</button>
|
||||
)}
|
||||
{alreadyInStore && (
|
||||
<>
|
||||
{isReleased && (
|
||||
<button
|
||||
className={`${buttonClass} bg-accent/70 text-white hover:bg-accent`}
|
||||
onClick={handleSeen}
|
||||
>
|
||||
{alreadyInStore.seen ? "Mark as unseen" : "Mark as seen"}
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
className={`${buttonClass} bg-amber-400/70 text-white hover:bg-amber-500`}
|
||||
onClick={handleFavorite}
|
||||
>
|
||||
{alreadyInStore.favorite
|
||||
? "Remove favorite"
|
||||
: "Add to favorites"}
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`${buttonClass} bg-primary/70 text-white hover:bg-primary`}
|
||||
onClick={handleRemove}
|
||||
>
|
||||
Remove from list
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<figure className="absolute inset-0 w-full bottom-[20%]">
|
||||
<img
|
||||
className="w-full h-96 object-cover"
|
||||
src={`http://image.tmdb.org/t/p/w342${poster_path}`}
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
"use client";
|
||||
import { FC } from "react";
|
||||
import { ReadMore } from "../../ReadMore";
|
||||
import { MdFavorite, MdFavoriteBorder } from "react-icons/md";
|
||||
import { RxEyeOpen, RxEyeClosed } from "react-icons/rx";
|
||||
import { IoMdRemoveCircleOutline } from "react-icons/io";
|
||||
import { Movie } from "@/types/global";
|
||||
|
||||
interface MinimalLayoutProps extends Movie {
|
||||
showDayCounter?: boolean;
|
||||
simpleToggle?: boolean;
|
||||
alreadyInStore?: Movie | undefined;
|
||||
isReleased: boolean;
|
||||
handleAdd: () => void;
|
||||
handleRemove: () => void;
|
||||
handleSeen: () => void;
|
||||
handleFavorite: () => void;
|
||||
releaseDate: Date;
|
||||
}
|
||||
|
||||
export const MinimalLayout: FC<MinimalLayoutProps> = ({
|
||||
title,
|
||||
overview,
|
||||
poster_path,
|
||||
vote_average,
|
||||
seen,
|
||||
favorite,
|
||||
alreadyInStore,
|
||||
isReleased,
|
||||
handleAdd,
|
||||
handleRemove,
|
||||
handleSeen,
|
||||
handleFavorite,
|
||||
releaseDate,
|
||||
}) => {
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
"use client";
|
||||
import { FC } from "react";
|
||||
import { ReadMore } from "../../ReadMore";
|
||||
import { MdFavorite, MdFavoriteBorder, MdOutlinePostAdd } from "react-icons/md";
|
||||
import { RxEyeOpen, RxEyeClosed } from "react-icons/rx";
|
||||
import { IoMdRemoveCircleOutline } from "react-icons/io";
|
||||
import { FaFire } from "react-icons/fa";
|
||||
import { RiCalendarCheckLine, RiCalendarScheduleLine } from "react-icons/ri";
|
||||
import { Movie } from "@/types/global";
|
||||
|
||||
interface ZeusLayoutProps extends Movie {
|
||||
showDayCounter?: boolean;
|
||||
simpleToggle?: boolean;
|
||||
alreadyInStore?: Movie | undefined;
|
||||
isReleased: boolean;
|
||||
handleAdd: () => void;
|
||||
handleRemove: () => void;
|
||||
handleSeen: () => void;
|
||||
handleFavorite: () => void;
|
||||
daysSinceRelease: number;
|
||||
releaseDate: Date;
|
||||
buttonClass: string;
|
||||
iconSize: number;
|
||||
}
|
||||
|
||||
export const ZeusLayout: FC<ZeusLayoutProps> = ({
|
||||
title,
|
||||
overview,
|
||||
popularity,
|
||||
poster_path,
|
||||
vote_average,
|
||||
seen,
|
||||
favorite,
|
||||
showDayCounter,
|
||||
simpleToggle,
|
||||
alreadyInStore,
|
||||
isReleased,
|
||||
handleAdd,
|
||||
handleRemove,
|
||||
handleSeen,
|
||||
handleFavorite,
|
||||
daysSinceRelease,
|
||||
releaseDate,
|
||||
buttonClass,
|
||||
iconSize,
|
||||
}) => {
|
||||
return (
|
||||
<article className="flex flex-col w-full shadow-lg rounded-t-lg overflow-hidden bg-black/50 shadow-white/5">
|
||||
<figure className="relative ">
|
||||
<img
|
||||
style={{
|
||||
aspectRatio: "342/513",
|
||||
}}
|
||||
className="w-full object-cover"
|
||||
src={`http://image.tmdb.org/t/p/w342${poster_path}`}
|
||||
/>
|
||||
<span className="absolute inset-0 bg-black/30 backdrop-blur-md opacity-0 hover-any:opacity-100 transition-opacity duration-300 flex items-center justify-center cursor-pointer">
|
||||
{!alreadyInStore && (
|
||||
<button className={buttonClass} onClick={handleAdd}>
|
||||
<MdOutlinePostAdd size={64} color="white" />
|
||||
</button>
|
||||
)}
|
||||
{alreadyInStore && (
|
||||
<div className="flex flex-col">
|
||||
<>
|
||||
{isReleased && !simpleToggle && (
|
||||
<button
|
||||
className={`${buttonClass} text-white`}
|
||||
onClick={handleSeen}
|
||||
>
|
||||
<span className="group-hover/toggle:hidden">
|
||||
{seen ? (
|
||||
<RxEyeOpen size={iconSize} />
|
||||
) : (
|
||||
<RxEyeClosed size={iconSize} />
|
||||
)}
|
||||
</span>
|
||||
<span className="hidden group-hover/toggle:block">
|
||||
{seen ? (
|
||||
<RxEyeClosed size={iconSize} />
|
||||
) : (
|
||||
<RxEyeOpen size={iconSize} />
|
||||
)}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
{!simpleToggle && (
|
||||
<button
|
||||
className={`${buttonClass} text-amber-400`}
|
||||
onClick={handleFavorite}
|
||||
>
|
||||
<span className="group-hover/toggle:hidden">
|
||||
{favorite ? (
|
||||
<MdFavorite size={iconSize} />
|
||||
) : (
|
||||
<MdFavoriteBorder size={iconSize} />
|
||||
)}
|
||||
</span>
|
||||
<span className="hidden group-hover/toggle:block">
|
||||
{favorite ? (
|
||||
<MdFavoriteBorder size={iconSize} />
|
||||
) : (
|
||||
<MdFavorite size={iconSize} />
|
||||
)}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
className={`${buttonClass} text-red-500`}
|
||||
onClick={handleRemove}
|
||||
>
|
||||
<IoMdRemoveCircleOutline size={iconSize} />
|
||||
</button>
|
||||
</>
|
||||
</div>
|
||||
)}
|
||||
</span>
|
||||
<span className="absolute top-0 right-0 bg-black/50 px-2 py-1 rounded-bl-lg">
|
||||
<p className="text-sm text-white flex items-center gap-1">
|
||||
<FaFire />
|
||||
{popularity}
|
||||
</p>
|
||||
</span>
|
||||
</figure>
|
||||
<div className="p-4">
|
||||
{!!vote_average && (
|
||||
<p className="flex items-center gap-1 text-sm mb-2">
|
||||
<span className="text-yellow-400">★</span>
|
||||
<span>{vote_average.toFixed(1)}/10</span>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="flex justify-between">
|
||||
<h2 className="text-xl leading-[1.1] font-bold">{title}</h2>
|
||||
</div>
|
||||
|
||||
<p
|
||||
className={`text-sm mt-2 flex items-center gap-1 leading-[1.1] ${
|
||||
isReleased ? "text-green-700" : "text-yellow-500"
|
||||
}`}
|
||||
>
|
||||
{isReleased ? <RiCalendarCheckLine /> : <RiCalendarScheduleLine />}
|
||||
<span>
|
||||
{releaseDate.toLocaleDateString("pl-PL", {
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
})}
|
||||
</span>
|
||||
</p>
|
||||
{showDayCounter && (
|
||||
<span className="text-xs text-gray-400">
|
||||
{isReleased
|
||||
? `${daysSinceRelease} dni od premiery`
|
||||
: `${daysSinceRelease} dni do premiery`}
|
||||
</span>
|
||||
)}
|
||||
<div className="text-xs text-gray-400 mt-4">
|
||||
<ReadMore text={overview} />
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
export { AuroraLayout } from "./AuroraLayout";
|
||||
export { MinimalLayout } from "./MinimalLayout";
|
||||
export { ZeusLayout } from "./ZeusLayout";
|
||||
export { DefaultLayout } from "./DefaultLayout";
|
||||
Loading…
Reference in New Issue