moviebox/src/components/molecules/HeroMovie/index.tsx

250 lines
9.4 KiB
TypeScript

"use client";
import { BackButton } from "@/components/atoms/BackButton";
import { Button } from "@/components/atoms/Button";
import { GenreLabel } from "@/components/atoms/GenreLabel";
import { MovieDetailsRich } from "@/lib/tmdb/types";
import { useGlobalStore } from "@/app/store/globalStore";
import { FC } from "react";
import {
FaHeart,
FaBookmark,
FaClock,
FaCalendar,
FaGlobe,
FaEye,
} from "react-icons/fa";
type Props = {
movieDetails: MovieDetailsRich;
};
export const HeroMovie: FC<Props> = ({ movieDetails }) => {
const { movies, addMovie, deleteMovie, updateMovie } = useGlobalStore();
// Check if movie is in store and get its state.
const movieInStore = movies.find((m) => m.id === movieDetails.id);
const isInStore = !!movieInStore;
const isFavorite = movieInStore?.favorite || false;
const isSeen = movieInStore?.seen || false;
const formatRuntime = (minutes: number) => {
const hours = Math.floor(minutes / 60);
const mins = minutes % 60;
return `${hours}h ${mins}m`;
};
// Convert TMDB movie to our Movie type.
const convertToMovie = () => ({
id: movieDetails.id,
title: movieDetails.title,
adult: movieDetails.adult,
backdrop_path: movieDetails.backdrop_path || "",
genre_ids: movieDetails.genres.map((g) => g.id).join(","),
original_language: movieDetails.original_language,
original_title: movieDetails.original_title,
overview: movieDetails.overview || "",
popularity: movieDetails.popularity,
poster_path: movieDetails.poster_path || "",
release_date: movieDetails.release_date,
video: movieDetails.video,
vote_average: movieDetails.vote_average,
vote_count: movieDetails.vote_count,
favorite: false,
seen: false,
});
const handleAddToList = () => {
if (isInStore) {
deleteMovie(movieDetails.id);
} else {
addMovie(convertToMovie());
}
};
const handleToggleFavorite = () => {
if (!isInStore) {
addMovie({ ...convertToMovie(), favorite: true });
} else {
updateMovie(movieDetails.id, { favorite: !isFavorite });
}
};
const handleToggleSeen = () => {
if (!isInStore) {
addMovie({ ...convertToMovie(), seen: true });
} else {
updateMovie(movieDetails.id, { seen: !isSeen });
}
};
return (
<section className="my-16">
<div className="relative">
{/* Navigation */}
<div className="absolute top-0 left-0 right-0 z-20 px-6">
<BackButton />
</div>
{/* Main content */}
<div className="relative z-10 px-6 lg:px-8">
<div className="max-w-7xl mx-auto">
<div className="flex flex-col lg:flex-row gap-8">
{/* Movie poster */}
<div className="flex-shrink-0">
<div className="relative group">
<img
src={`https://image.tmdb.org/t/p/w500${movieDetails.poster_path}`}
alt={movieDetails.title}
className="w-80 h-auto rounded-2xl shadow-2xl shadow-purple-500/20 group-hover:shadow-purple-500/40 transition-all duration-500"
/>
<div className="absolute inset-0 rounded-2xl bg-gradient-to-t from-purple-600/20 to-transparent opacity-100" />
</div>
</div>
{/* Movie details */}
<div className="flex-1 text-white">
<div className="space-y-6">
{/* Title and rating */}
<div>
<h1 className="text-4xl lg:text-5xl font-bold bg-gradient-to-r from-white to-gray-300 bg-clip-text text-transparent mb-3">
{movieDetails.title}
</h1>
{movieDetails.tagline && (
<p className="text-xl text-gray-300 italic mb-4">
"{movieDetails.tagline}"
</p>
)}
<div className="flex items-center gap-6 flex-wrap">
<div className="flex items-center gap-2">
<div className="flex">
{[...Array(5)].map((_, i) => (
<span
key={i}
className={`text-2xl ${
i < Math.round(movieDetails.vote_average / 2)
? "text-yellow-400"
: "text-gray-600"
}`}
>
</span>
))}
</div>
<span className="text-lg font-semibold">
{movieDetails.vote_average.toFixed(1)}
</span>
<span className="text-gray-400">
({movieDetails.vote_count} głosów)
</span>
</div>
</div>
</div>
{/* Key info */}
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
{movieDetails.release_date && (
<div className="flex items-center gap-2 text-gray-300">
<FaCalendar className="text-purple-400" />
<span>
{new Date(movieDetails.release_date).getFullYear()}
</span>
</div>
)}
{movieDetails.runtime && (
<div className="flex items-center gap-2 text-gray-300">
<FaClock className="text-purple-400" />
<span>{formatRuntime(movieDetails.runtime)}</span>
</div>
)}
{movieDetails.spoken_languages[0] && (
<div className="flex items-center gap-2 text-gray-300">
<FaGlobe className="text-purple-400" />
<span>{movieDetails.spoken_languages[0].name}</span>
</div>
)}
<div className="flex items-center gap-2 text-gray-300">
<span className="text-purple-400">Status:</span>
<span>{movieDetails.status}</span>
</div>
</div>
{/* Genres */}
{movieDetails.genres.length > 0 && (
<div>
<h3 className="text-lg font-semibold mb-3 text-purple-300">
Gatunki
</h3>
<div className="flex flex-wrap gap-2">
{movieDetails.genres.map((genre) => (
<GenreLabel key={genre.id} genre={genre.name} />
))}
</div>
</div>
)}
{/* Synopsis */}
{movieDetails.overview && (
<div>
<h3 className="text-lg font-semibold mb-3 text-purple-300">
Opis
</h3>
<p className="text-gray-300 leading-relaxed text-lg">
{movieDetails.overview}
</p>
</div>
)}
{/* Action buttons */}
<div className="flex gap-4 flex-wrap">
<Button
className={`flex items-center gap-3 ${
isInStore
? "bg-gradient-to-r from-emerald-600 to-teal-600 hover:from-emerald-500 hover:to-teal-500"
: ""
}`}
onClick={handleAddToList}
>
<FaBookmark />
{isInStore ? "Usuń z listy" : "Dodaj do listy"}
</Button>
<Button
theme="glass"
className={`flex items-center gap-3 ${
isFavorite
? "bg-gradient-to-r from-rose-600/90 to-pink-600/90 hover:from-rose-500/90 hover:to-pink-500/90 border-rose-400/30"
: ""
}`}
onClick={handleToggleFavorite}
>
<FaHeart className={isFavorite ? "text-rose-200" : ""} />
{isFavorite ? "Usuń z ulubionych" : "Dodaj do ulubionych"}
</Button>
<Button
theme="glass"
className={`flex items-center gap-3 ${
isSeen
? "bg-gradient-to-r from-emerald-600/90 to-teal-600/90 hover:from-emerald-500/90 hover:to-teal-500/90 border-emerald-400/30"
: ""
}`}
onClick={handleToggleSeen}
>
<FaEye className={isSeen ? "text-emerald-200" : ""} />
{isSeen
? "Oznacz jako nieobejrzany"
: "Oznacz jako obejrzany"}
</Button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
);
};