feat: implement responsive container utility and enhance layout styles across components; update Carousel and Gallery for improved structure and consistency

This commit is contained in:
Norbert Maciaszek 2025-08-18 15:27:28 +02:00
parent e891b37384
commit fd1240252d
17 changed files with 353 additions and 345 deletions

View File

@ -16,22 +16,30 @@ export default async function Page({
const personDetails = await TMDB.getPersonDetails(actorId); const personDetails = await TMDB.getPersonDetails(actorId);
return ( return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 pb-16"> <div className="bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 py-0.5">
<ActorHero personDetails={personDetails} /> <ActorHero personDetails={personDetails} />
<Gallery images={personDetails.images.profiles} heading="Galeria" /> <Gallery images={personDetails.images.profiles} heading="Galeria" />
<div className="container"> <section className="blocks">
<Carousel <div className="container">
heading={`Filmy z udziałem ${personDetails.name}`} <Carousel
icon={<FaStar />} heading={`Filmy z udziałem ${personDetails.name}`}
colors="purple" icon={<FaStar />}
> colors="purple"
{personDetails.movie_credits.cast.map((movie) => { >
const convertedMovie = convertToMovie(movie); {personDetails.movie_credits.cast
if (!convertedMovie) return null; .sort(
return <MovieCard key={movie.id} {...convertedMovie} />; (a, b) =>
})} new Date(b.release_date).getTime() -
</Carousel> new Date(a.release_date).getTime()
</div> )
.map((movie) => {
const convertedMovie = convertToMovie(movie);
if (!convertedMovie) return null;
return <MovieCard key={movie.id} {...convertedMovie} />;
})}
</Carousel>
</div>
</section>
</div> </div>
); );
} }

View File

@ -12,10 +12,10 @@ export default async function Page({
const movieDetails = await TMDB.getMovieDetails(movieId); const movieDetails = await TMDB.getMovieDetails(movieId);
return ( return (
<div className="min-h-screen mt-16"> <>
<HeroMovie movieDetails={movieDetails} /> <HeroMovie movieDetails={movieDetails} />
<MovieCast movieDetails={movieDetails} /> <MovieCast movieDetails={movieDetails} />
<RecommendedMovies movies={movieDetails.recommendations} /> <RecommendedMovies movies={movieDetails.recommendations} />
</div> </>
); );
} }

View File

@ -46,18 +46,30 @@
@custom-variant hover-any (&:hover); @custom-variant hover-any (&:hover);
@utility container {
margin: 0 auto;
padding: 0 15px;
width: 100%;
max-width: 100%;
@variant sm {
max-width: 640px;
}
@variant lg {
max-width: 860px;
}
@variant xl {
max-width: 1280px;
}
}
@layer base { @layer base {
body { body {
@apply bg-hippie-blue-950 text-text; @apply bg-hippie-blue-950 text-text;
} }
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 0 15px;
}
.container-fluid { .container-fluid {
width: 100%; width: 100%;
max-width: 100%; max-width: 100%;
@ -71,6 +83,22 @@
margin: 0 -15px; margin: 0 -15px;
} }
.blocks {
margin: 32px 0;
@variant lg {
margin: 64px 0;
}
}
.blockp {
padding: 32px 0;
@variant lg {
padding: 64px 0;
}
}
a { a {
@apply transition-colors; @apply transition-colors;
} }

View File

@ -34,9 +34,7 @@ export default async function RootLayout({
<GlobalStoreProvider> <GlobalStoreProvider>
<AuroraBackground /> <AuroraBackground />
<Navbar /> <Navbar />
<main className="relative pt-16 lg:pt-20 [&>:last-child]:mb-0"> <main className="relative">{children}</main>
{children}
</main>
</GlobalStoreProvider> </GlobalStoreProvider>
</body> </body>
</html> </html>

View File

@ -47,63 +47,41 @@ export default async function Home() {
<> <>
<Hero movies={heroMovies} autoRotate /> <Hero movies={heroMovies} autoRotate />
<div className="container py-16 space-y-16"> <section className="blocks">
<section> <Carousel heading="Teraz w kinach" icon={<FaPlay />}>
<Carousel heading="Teraz w kinach" icon={<FaPlay />}> {nowPlayingMovies.map((movie) => (
{nowPlayingMovies.map((movie) => ( <MovieCard key={movie.id} {...movie} simpleToggle layout="aurora" />
<MovieCard ))}
key={movie.id} </Carousel>
{...movie} </section>
simpleToggle
layout="aurora"
/>
))}
</Carousel>
</section>
<section> <section className="blocks">
<Carousel <Carousel
heading="Nadchodzące filmy" heading="Nadchodzące filmy"
icon={<FaCalendar />} icon={<FaCalendar />}
colors="blue" colors="blue"
> >
{upcomingMovies.map((movie) => ( {upcomingMovies.map((movie) => (
<MovieCard <MovieCard key={movie.id} {...movie} simpleToggle layout="aurora" />
key={movie.id} ))}
{...movie} </Carousel>
simpleToggle </section>
layout="aurora"
/>
))}
</Carousel>
</section>
<section> <section className="blocks">
<Carousel heading="Popularne filmy" icon={<FaFire />} colors="red"> <Carousel heading="Popularne filmy" icon={<FaFire />} colors="red">
{popularMovies.map((movie) => ( {popularMovies.map((movie) => (
<MovieCard <MovieCard key={movie.id} {...movie} simpleToggle layout="aurora" />
key={movie.id} ))}
{...movie} </Carousel>
simpleToggle </section>
layout="aurora"
/>
))}
</Carousel>
</section>
<section> <section className="blocks">
<Carousel heading="Trendy dnia" icon={<FaChartLine />} colors="green"> <Carousel heading="Trendy dnia" icon={<FaChartLine />} colors="green">
{trendingMovies.map((movie) => ( {trendingMovies.map((movie) => (
<MovieCard <MovieCard key={movie.id} {...movie} simpleToggle layout="aurora" />
key={movie.id} ))}
{...movie} </Carousel>
simpleToggle </section>
layout="aurora"
/>
))}
</Carousel>
</section>
</div>
<div className="p-2 text-center text-xs text-gray-500"> <div className="p-2 text-center text-xs text-gray-500">
<p>Ostatnia aktualizacja: {lastModified}</p> <p>Ostatnia aktualizacja: {lastModified}</p>

View File

@ -7,7 +7,7 @@ type Props = {
children: React.ReactNode; children: React.ReactNode;
className?: string; className?: string;
theme?: Theme; theme?: Theme;
size?: "small" | "medium" | "large"; size?: "small" | "medium" | "large" | "icon";
onClick?: () => void; onClick?: () => void;
href?: string; href?: string;
gradient?: { gradient?: {
@ -31,7 +31,7 @@ export const Button: FC<Props> = ({
return ( return (
<Component <Component
className={`flex items-center justify-center gap-2 cursor-pointer text-white rounded-xl font-semibold shadow-2xl transition-all duration-300 hover:scale-105 className={`flex items-center justify-center gap-2 cursor-pointer text-white rounded-xl font-semibold shadow-2xl transition-colors duration-300
bg-gradient-to-r ${buttonColor?.from} ${buttonColor?.to} cursor-pointer ${sizes[size]} ${className}`} bg-gradient-to-r ${buttonColor?.from} ${buttonColor?.to} cursor-pointer ${sizes[size]} ${className}`}
onClick={onClick} onClick={onClick}
{...(href && { href })} {...(href && { href })}
@ -45,6 +45,7 @@ const sizes = {
small: "px-4 py-2 text-sm", small: "px-4 py-2 text-sm",
medium: "px-8 py-4 text-lg", medium: "px-8 py-4 text-lg",
large: "px-12 py-6 text-xl", large: "px-12 py-6 text-xl",
icon: "p-3 [&>*]:w-5 [&>*]:h-5",
}; };
const colors = { const colors = {
@ -57,7 +58,7 @@ const colors = {
to: "to-pink-600 hover:to-pink-500", to: "to-pink-600 hover:to-pink-500",
}, },
glass: { glass: {
from: "from-white/15 via-white/8 to-white/12", from: "from-white/15 via-white/8 to-white/12 border border-white/20",
to: "to-white/15 hover:to-white/10", to: "to-white/15 hover:to-white/10",
}, },
rose: { rose: {

View File

@ -48,7 +48,7 @@ export const AuroraLayout: FC<AuroraLayoutProps> = ({
: "from-red-400 to-pink-400"; : "from-red-400 to-pink-400";
return ( return (
<article className="group relative w-full overflow-hidden rounded-2xl"> <article className="group relative w-full overflow-hidden rounded-2xl max-w-[300px] mx-auto">
{/* Aurora background effect */} {/* 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-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> <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>

View File

@ -55,7 +55,7 @@ export const ActorHero: FC<Props> = ({ personDetails }) => {
}; };
return ( return (
<section className="pt-16 pb-8"> <section className="blocks">
<div className="relative"> <div className="relative">
{/* Navigation */} {/* Navigation */}
<div className="absolute top-0 left-0 right-0 z-20 px-6"> <div className="absolute top-0 left-0 right-0 z-20 px-6">
@ -63,161 +63,157 @@ export const ActorHero: FC<Props> = ({ personDetails }) => {
</div> </div>
{/* Main content */} {/* Main content */}
<div className="relative z-10 px-6 lg:px-8 mt-16"> <div className="container">
<div className="max-w-7xl mx-auto"> <div className="flex flex-col lg:flex-row gap-8">
<div className="flex flex-col lg:flex-row gap-8"> {/* Profile photo */}
{/* Profile photo */} <div className="flex-shrink-0 text-center">
<div className="flex-shrink-0"> <div className="relative group inline-block">
<div className="relative group"> <img
<img src={
src={ personDetails.profile_path
personDetails.profile_path ? `https://image.tmdb.org/t/p/w500${personDetails.profile_path}`
? `https://image.tmdb.org/t/p/w500${personDetails.profile_path}` : "/api/placeholder/400/600"
: "/api/placeholder/400/600" }
} alt={personDetails.name}
alt={personDetails.name} className="w-80 h-auto rounded-2xl shadow-2xl shadow-purple-500/20 group-hover:shadow-purple-500/40 transition-all duration-500"
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 className="absolute inset-0 rounded-2xl bg-gradient-to-t from-purple-600/20 to-transparent opacity-100" />
</div>
</div> </div>
</div>
{/* Actor details */} {/* Actor details */}
<div className="flex-1 text-white"> <div className="flex-1 text-white">
<div className="space-y-6"> <div className="space-y-6">
{/* Name and known for */} {/* Name and known for */}
<div> <div>
<h1 className="text-4xl lg:text-5xl font-bold bg-gradient-to-r from-white to-gray-300 bg-clip-text text-transparent"> <h1 className="text-4xl lg:text-5xl font-bold bg-gradient-to-r from-white to-gray-300 bg-clip-text text-transparent">
{personDetails.name} {personDetails.name}
</h1> </h1>
{personDetails.birthday && ( {personDetails.birthday && (
<span className="text-sm text-gray-400"> <span className="text-sm text-gray-400">
( (
{calculateAge( {calculateAge(
personDetails.birthday, personDetails.birthday,
personDetails.deathday personDetails.deathday
)}{" "} )}{" "}
lat lat
{personDetails.deathday && " w chwili śmierci"}) {personDetails.deathday && " w chwili śmierci"})
</span> </span>
)}
<div className="flex items-center gap-4 mb-4 mt-4">
<div className="flex items-center gap-2">
<FaTheaterMasks className="text-purple-400" />
<span className="text-xl text-gray-300 font-medium">
{personDetails.known_for_department}
</span>
</div>
<div className="flex items-center gap-2">
<FaStar className="text-yellow-400" />
<span className="text-lg text-gray-300">
{Math.round(personDetails.popularity)} popularność
</span>
</div>
</div>
{/* Also known as */}
{personDetails.also_known_as.length > 0 && (
<div className="mb-4">
<p className="text-gray-400 text-sm">
Znany również jako:{" "}
{personDetails.also_known_as.slice(0, 3).join(", ")}
</p>
</div>
)}
</div>
{/* Personal info */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{/* Gender */}
<div className="flex items-center gap-2 text-gray-300">
<span className="text-purple-400">Płeć:</span>
<span>{getGenderText(personDetails.gender)}</span>
</div>
{/* Birthday */}
{personDetails.birthday && (
<div className="flex items-center gap-2 text-gray-300">
<FaCalendarAlt className="text-purple-400" />
<div className="flex">
<span>{formatDate(personDetails.birthday)}</span>
</div>
</div>
)}
{/* Deathday */}
{personDetails.deathday && (
<div className="flex items-center gap-2 text-gray-300">
<FaCalendarAlt className="text-red-400" />
<div className="flex flex-col">
<span className="text-red-300">Data śmierci:</span>
<span>{formatDate(personDetails.deathday)}</span>
</div>
</div>
)}
{/* Place of birth */}
{personDetails.place_of_birth && (
<div className="flex items-center gap-2 text-gray-300">
<FaMapMarkerAlt className="text-purple-400" />
<span>{personDetails.place_of_birth}</span>
</div>
)}
</div>
{/* Biography */}
{personDetails.biography && (
<div>
<h3 className="text-lg font-semibold mb-3 text-purple-300">
Biografia
</h3>
<div className="text-gray-300 leading-relaxed text-lg space-y-4">
{personDetails.biography
.split("\n\n")
.map((paragraph, index) => (
<p key={index}>{paragraph}</p>
))}
</div>
</div>
)} )}
{/* External links */} <div className="flex items-center gap-4 mb-4 mt-4">
{personDetails.external_ids && ( <div className="flex items-center gap-2">
<div> <FaTheaterMasks className="text-purple-400" />
<h3 className="text-lg font-semibold mb-3 text-purple-300"> <span className="text-xl text-gray-300 font-medium">
Linki {personDetails.known_for_department}
</h3> </span>
<div className="flex gap-4"> </div>
{Object.entries(personDetails.external_ids).map(
([key, value]) => {
if (!(key in externalIdsMap) || !value) {
return null;
}
const { label, icon, url } = <div className="flex items-center gap-2">
externalIdsMap[ <FaStar className="text-yellow-400" />
key as keyof typeof externalIdsMap <span className="text-lg text-gray-300">
]; {Math.round(personDetails.popularity)} popularność
return ( </span>
<a </div>
key={key} </div>
href={url(value as string)}
target="_blank" {/* Also known as */}
rel="noopener noreferrer" {personDetails.also_known_as.length > 0 && (
className="flex items-center gap-2 bg-white/10 hover:bg-white/20 px-4 py-2 rounded-xl transition-all duration-300 border border-white/20" <div className="mb-4">
> <p className="text-gray-400 text-sm">
{icon} Znany również jako:{" "}
{label} {personDetails.also_known_as.slice(0, 3).join(", ")}
</a> </p>
);
}
)}
</div>
</div> </div>
)} )}
</div> </div>
{/* Personal info */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{/* Gender */}
<div className="flex items-center gap-2 text-gray-300">
<span className="text-purple-400">Płeć:</span>
<span>{getGenderText(personDetails.gender)}</span>
</div>
{/* Birthday */}
{personDetails.birthday && (
<div className="flex items-center gap-2 text-gray-300">
<FaCalendarAlt className="text-purple-400" />
<div className="flex">
<span>{formatDate(personDetails.birthday)}</span>
</div>
</div>
)}
{/* Deathday */}
{personDetails.deathday && (
<div className="flex items-center gap-2 text-gray-300">
<FaCalendarAlt className="text-red-400" />
<div className="flex flex-col">
<span className="text-red-300">Data śmierci:</span>
<span>{formatDate(personDetails.deathday)}</span>
</div>
</div>
)}
{/* Place of birth */}
{personDetails.place_of_birth && (
<div className="flex items-center gap-2 text-gray-300">
<FaMapMarkerAlt className="text-purple-400" />
<span>{personDetails.place_of_birth}</span>
</div>
)}
</div>
{/* Biography */}
{personDetails.biography && (
<div>
<h3 className="text-lg font-semibold mb-3 text-purple-300">
Biografia
</h3>
<div className="text-gray-300 leading-relaxed text-lg space-y-4">
{personDetails.biography
.split("\n\n")
.map((paragraph, index) => (
<p key={index}>{paragraph}</p>
))}
</div>
</div>
)}
{/* External links */}
{personDetails.external_ids && (
<div>
<h3 className="text-lg font-semibold mb-3 text-purple-300">
Linki
</h3>
<div className="flex gap-4">
{Object.entries(personDetails.external_ids).map(
([key, value]) => {
if (!(key in externalIdsMap) || !value) {
return null;
}
const { label, icon, url } =
externalIdsMap[key as keyof typeof externalIdsMap];
return (
<a
key={key}
href={url(value as string)}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-2 bg-white/10 hover:bg-white/20 px-4 py-2 rounded-xl transition-all duration-300 border border-white/20"
>
{icon}
{label}
</a>
);
}
)}
</div>
</div>
)}
</div> </div>
</div> </div>
</div> </div>

View File

@ -15,6 +15,7 @@ type Props = {
heading?: string; heading?: string;
icon?: ReactNode; icon?: ReactNode;
colors?: keyof typeof colorsMap; colors?: keyof typeof colorsMap;
fluid?: boolean;
}; };
export const Carousel: FC<Props> = ({ export const Carousel: FC<Props> = ({
@ -24,6 +25,7 @@ export const Carousel: FC<Props> = ({
heading, heading,
icon, icon,
colors = "yellow", colors = "yellow",
fluid = false,
}) => { }) => {
const { const {
itemsPerView = 4, itemsPerView = 4,
@ -66,17 +68,19 @@ export const Carousel: FC<Props> = ({
); );
return ( return (
<div> <div className={`${fluid ? "max-w-full px-4" : "container"}`}>
<div className="flex items-center justify-between mb-8"> <div className="flex items-center justify-between mb-8 flex-wrap">
{heading && ( {heading && (
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
{icon && ( {icon && (
<div className={`p-2 rounded-lg ${colorsMap[colors]}`}> <div
className={`hidden sm:block p-2 rounded-lg ${colorsMap[colors]}`}
>
{icon} {icon}
</div> </div>
)} )}
<h2 <h2
className={`text-3xl font-bold ${colorsMap[colors]} bg-clip-text text-transparent`} className={`text-3xl font-bold ${colorsMap[colors]} bg-clip-text text-transparent text-center sm:text-left`}
> >
{heading} {heading}
</h2> </h2>
@ -84,7 +88,7 @@ export const Carousel: FC<Props> = ({
)} )}
{totalPages > 1 && ( {totalPages > 1 && (
<div className="flex gap-2"> <div className="flex gap-2 mx-auto mt-4 sm:mt-0 sm:mr-0 sm:ml-auto">
<button <button
onClick={prevPage} onClick={prevPage}
className={`cursor-pointer p-3 rounded-full ${colorsMap[colors]} text-white transition-all duration-300`} className={`cursor-pointer p-3 rounded-full ${colorsMap[colors]} text-white transition-all duration-300`}
@ -116,7 +120,7 @@ export const Carousel: FC<Props> = ({
{totalPages > 1 && ( {totalPages > 1 && (
<div className="flex justify-center mt-8"> <div className="flex justify-center mt-8">
<div className="flex gap-2"> <div className="flex gap-2 flex-wrap justify-center">
{Array.from({ length: totalPages }, (_, i) => ( {Array.from({ length: totalPages }, (_, i) => (
<button <button
key={i} key={i}

View File

@ -43,71 +43,69 @@ export const Gallery: FC<Props> = ({
}; };
return ( return (
<section className="py-16"> <section className="blocks">
<div className="px-6 lg:px-8"> <div className="container">
<div className="max-w-7xl mx-auto"> {heading && (
{heading && ( <div className="flex items-center gap-3 mb-8">
<div className="flex items-center gap-3 mb-8"> <div className="p-2 rounded-lg bg-gradient-to-r from-purple-500 to-pink-500">
<div className="p-2 rounded-lg bg-gradient-to-r from-purple-500 to-pink-500"> <FaImages className="text-white" size={20} />
<FaImages className="text-white" size={20} />
</div>
<h2 className="text-3xl font-bold bg-gradient-to-r from-purple-400 to-pink-400 bg-clip-text text-transparent">
{heading}
</h2>
</div> </div>
)} <h2 className="text-3xl font-bold bg-gradient-to-r from-purple-400 to-pink-400 bg-clip-text text-transparent">
{heading}
{/* Category tabs */} </h2>
{categories.length > 0 && (
<div className="flex gap-4 mb-8">
{Object.entries(images).map(([category, categoryImages]) => {
if (!categoryImages.length) return null;
return (
<button
key={category}
onClick={() =>
setSelectedCategory(category as keyof typeof images)
}
className={`px-6 py-3 rounded-xl font-medium transition-all duration-300 ${
selectedCategory === category
? "bg-gradient-to-r from-purple-600 to-pink-600 text-white shadow-lg"
: "bg-white/10 text-gray-300 hover:bg-white/20 hover:text-white"
}`}
>
{category} ({categoryImages.length})
</button>
);
})}
</div>
)}
{/* Image grid */}
<div className={`grid gap-4 grid-auto-cols-160 [&>div]:contents`}>
<LightGallery>
{currentImages.slice(0, limit).map((image, index) => (
<a
key={index}
href={getImageUrl(image.file_path)}
className="group relative overflow-hidden rounded-xl cursor-pointer bg-slate-800"
>
<img src={getImageUrl(image.file_path, "w185")} />
</a>
))}
</LightGallery>
</div> </div>
{limit < currentImages.length && ( )}
<div className="flex justify-center mt-6">
<Button {/* Category tabs */}
theme="teal" {categories.length > 0 && (
size="small" <div className="flex gap-4 mb-8">
onClick={() => setLimit(currentImages.length)} {Object.entries(images).map(([category, categoryImages]) => {
if (!categoryImages.length) return null;
return (
<button
key={category}
onClick={() =>
setSelectedCategory(category as keyof typeof images)
}
className={`px-6 py-3 rounded-xl font-medium transition-all duration-300 ${
selectedCategory === category
? "bg-gradient-to-r from-purple-600 to-pink-600 text-white shadow-lg"
: "bg-white/10 text-gray-300 hover:bg-white/20 hover:text-white"
}`}
>
{category} ({categoryImages.length})
</button>
);
})}
</div>
)}
{/* Image grid */}
<div className={`grid gap-4 grid-auto-cols-160 [&>div]:contents`}>
<LightGallery>
{currentImages.slice(0, limit).map((image, index) => (
<a
key={index}
href={getImageUrl(image.file_path)}
className="group relative overflow-hidden rounded-xl cursor-pointer bg-slate-800"
> >
Pokaż wszystkie ({currentImages.length - limit}) <img src={getImageUrl(image.file_path, "w185")} />
</Button> </a>
</div> ))}
)} </LightGallery>
</div> </div>
{limit < currentImages.length && (
<div className="flex justify-center mt-6">
<Button
theme="teal"
size="small"
onClick={() => setLimit(currentImages.length)}
>
Pokaż wszystkie ({currentImages.length - limit})
</Button>
</div>
)}
</div> </div>
</section> </section>
); );

View File

@ -75,7 +75,7 @@ export const HeroMovie: FC<Props> = ({ movieDetails }) => {
}; };
return ( return (
<section className="my-16"> <section className="blocks">
<div className="relative"> <div className="relative">
{/* Navigation */} {/* Navigation */}
<div className="absolute top-0 left-0 right-0 z-20 px-6"> <div className="absolute top-0 left-0 right-0 z-20 px-6">
@ -83,8 +83,8 @@ export const HeroMovie: FC<Props> = ({ movieDetails }) => {
</div> </div>
{/* Main content */} {/* Main content */}
<div className="relative z-10 px-6 lg:px-8"> <div className="relative z-10">
<div className="max-w-7xl mx-auto"> <div className="container">
<div className="flex flex-col lg:flex-row gap-8"> <div className="flex flex-col lg:flex-row gap-8">
{/* Movie poster */} {/* Movie poster */}
<div className="flex-shrink-0"> <div className="flex-shrink-0">

View File

@ -23,14 +23,14 @@ export const MovieCast: FC<Props> = ({ movieDetails }) => {
}).format(amount); }).format(amount);
return ( return (
<section className="px-6 lg:px-8 py-16"> <section className="blocks">
<div className="max-w-7xl mx-auto"> <div className="container mx-auto">
<div className="grid lg:grid-cols-3 gap-12"> <div className="grid lg:grid-cols-3 gap-12">
{/* Cast */} {/* Cast */}
{mainCast.length > 0 && ( {mainCast.length > 0 && (
<div className="lg:col-span-2"> <div className="lg:col-span-2">
<h2 className="text-2xl font-bold text-white mb-6">Obsada</h2> <h2 className="text-2xl font-bold text-white mb-6">Obsada</h2>
<div className="grid grid-cols-2 md:grid-cols-4 gap-6"> <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-6">
{mainCast.map((actor) => ( {mainCast.map((actor) => (
<Link <Link
key={actor.id} key={actor.id}

View File

@ -78,8 +78,8 @@ export const MovieList: FC<Props> = ({
} }
return ( return (
<section className="my-4 md:my-10"> <section className="blocks">
<div className={`${fluid ? "max-w-full" : "container"}`}> <div className={`${fluid ? "max-w-full px-4" : "container"}`}>
{heading && ( {heading && (
<div className="row"> <div className="row">
<div className="col-12 md:col-10 flex gap-2 items-center"> <div className="col-12 md:col-10 flex gap-2 items-center">
@ -103,7 +103,7 @@ export const MovieList: FC<Props> = ({
)} )}
{filteredMovies.length > 0 && ( {filteredMovies.length > 0 && (
<div <div
className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 gap-y-6 gap-3 sm:gap-6 mt-8 justify-center" className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-y-6 gap-3 sm:gap-6 mt-8 justify-center"
ref={parent} ref={parent}
> >
{sortedMovies.map((movie) => ( {sortedMovies.map((movie) => (

View File

@ -13,15 +13,20 @@ export const RecommendedMovies: FC<Props> = ({ movies }) => {
if (!movies.results.length) return null; if (!movies.results.length) return null;
return ( return (
<section className="py-16 bg-gradient-to-br from-slate-900/50 to-slate-800/50"> <section className="blockp bg-gradient-to-br from-slate-900/50 to-slate-800/50">
<div className="px-6 lg:px-8"> <div className="container">
<div className="max-w-7xl mx-auto"> <Carousel
<Carousel heading="Rekomendowane filmy"
heading="Rekomendowane filmy" icon={<FaStar />}
icon={<FaStar />} colors="yellow"
colors="yellow" >
> {movies.results
{movies.results.map((movie) => ( .sort(
(a, b) =>
new Date(b.release_date).getTime() -
new Date(a.release_date).getTime()
)
.map((movie) => (
<MovieCard <MovieCard
key={movie.id} key={movie.id}
layout="aurora" layout="aurora"
@ -43,8 +48,7 @@ export const RecommendedMovies: FC<Props> = ({ movies }) => {
favorite={false} favorite={false}
/> />
))} ))}
</Carousel> </Carousel>
</div>
</div> </div>
</section> </section>
); );

View File

@ -32,7 +32,7 @@ export const SimilarMovies: FC<Props> = ({ movies }) => {
return ( return (
<section className="py-16"> <section className="py-16">
<div className="px-6 lg:px-8"> <div className="px-6 lg:px-8">
<div className="max-w-7xl mx-auto"> <div className="container mx-auto">
<div className="flex items-center justify-between mb-8"> <div className="flex items-center justify-between mb-8">
<h2 className="text-3xl font-bold bg-gradient-to-r from-white to-gray-300 bg-clip-text text-transparent"> <h2 className="text-3xl font-bold bg-gradient-to-r from-white to-gray-300 bg-clip-text text-transparent">
Podobne filmy Podobne filmy

View File

@ -49,16 +49,12 @@ export const Search = () => {
return ( return (
<> <>
<Button <Button
theme="glass" theme="secondary"
size="small" size="icon"
className="group relative" className="group relative"
onClick={() => setIsSearchOpen(!isSearchOpen)} onClick={() => setIsSearchOpen(!isSearchOpen)}
> >
<IoSearch <IoSearch className="text-white" />
size={20}
className="text-white transition-transform duration-300 group-hover:scale-110"
/>
<div className="absolute inset-0 bg-gradient-to-r from-purple-500/30 to-cyan-500/30 rounded-xl opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
</Button> </Button>
{isSearchOpen && ( {isSearchOpen && (
@ -71,14 +67,11 @@ export const Search = () => {
{/* Close button */} {/* Close button */}
<Button <Button
theme="glass" theme="glass"
size="small" size="icon"
className="absolute top-6 right-6 z-10 group hover:!bg-red-500/50" className="absolute top-6 right-6 z-10 group hover:!bg-red-500/50"
onClick={handleClose} onClick={handleClose}
> >
<IoClose <IoClose className="text-white transition-transform duration-300 group-hover:rotate-90" />
size={24}
className="text-white transition-transform duration-300 group-hover:rotate-90"
/>
</Button> </Button>
<div className="relative min-h-full flex flex-col py-20" ref={ref}> <div className="relative min-h-full flex flex-col py-20" ref={ref}>

View File

@ -4,6 +4,7 @@ import { useEffect, useState } from "react";
import { HiMenuAlt3, HiX, HiSparkles } from "react-icons/hi"; import { HiMenuAlt3, HiX, HiSparkles } from "react-icons/hi";
import { Search } from "./components/Search"; import { Search } from "./components/Search";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
import { Button } from "@/components/atoms/Button";
const links = [ const links = [
{ {
@ -33,7 +34,7 @@ export const Navbar = () => {
return ( return (
<> <>
{/* Main Navbar */} {/* Main Navbar */}
<header className="fixed top-0 w-full z-50 transition-all duration-300"> <header className="sticky top-0 w-full z-50 transition-all duration-300">
{/* Aurora background effect */} {/* Aurora background effect */}
<div className="absolute inset-0 bg-gradient-to-r from-slate-900/95 via-slate-800/98 to-slate-900/95"></div> <div className="absolute inset-0 bg-gradient-to-r from-slate-900/95 via-slate-800/98 to-slate-900/95"></div>
<div className="absolute inset-0 bg-gradient-to-r from-purple-600/15 via-blue-600/10 to-teal-600/15"></div> <div className="absolute inset-0 bg-gradient-to-r from-purple-600/15 via-blue-600/10 to-teal-600/15"></div>
@ -43,7 +44,7 @@ export const Navbar = () => {
<div className="absolute bottom-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-purple-400/50 to-transparent"></div> <div className="absolute bottom-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-purple-400/50 to-transparent"></div>
<div className="relative"> <div className="relative">
<div className="container mx-auto px-4"> <div className=" mx-auto px-4 max-w-[1500px]">
<div className="flex items-center justify-between h-16 lg:h-20"> <div className="flex items-center justify-between h-16 lg:h-20">
{/* Logo */} {/* Logo */}
<Link <Link
@ -95,19 +96,18 @@ export const Navbar = () => {
<Search /> <Search />
{/* Mobile Menu Button */} {/* Mobile Menu Button */}
<button <Button
theme="glass"
size="icon"
onClick={toggleMobileMenu} onClick={toggleMobileMenu}
className="md:hidden relative p-3 rounded-xl bg-gradient-to-br from-white/15 to-white/5 border border-white/20 transition-all duration-300 hover:bg-gradient-to-br hover:from-white/25 hover:to-white/10 hover:scale-105 shadow-lg" className="md:hidden"
> >
<div className="w-6 h-6 flex items-center justify-center"> {isMobileMenuOpen ? (
{isMobileMenuOpen ? ( <HiX className="text-white" />
<HiX className="w-5 h-5 text-white" /> ) : (
) : ( <HiMenuAlt3 className="text-white" />
<HiMenuAlt3 className="w-5 h-5 text-white" /> )}
)} </Button>
</div>
<div className="absolute inset-0 bg-gradient-to-r from-purple-500/30 to-cyan-500/30 rounded-xl opacity-0 hover:opacity-100 transition-opacity duration-300"></div>
</button>
</div> </div>
</div> </div>
</div> </div>