feat: add GenreList component for genre exploration and integrate it into Home and Odkrywaj pages; update GenreLabel to support linking by genre ID
This commit is contained in:
parent
653380f0fb
commit
59444e131a
|
|
@ -7,6 +7,7 @@ import {
|
||||||
import { Hero } from "@/components/organisms/Hero";
|
import { Hero } from "@/components/organisms/Hero";
|
||||||
import { FaCalendar, FaChartLine, FaFire, FaPlay } from "react-icons/fa";
|
import { FaCalendar, FaChartLine, FaFire, FaPlay } from "react-icons/fa";
|
||||||
import { MovieList } from "@/components/molecules/MovieList";
|
import { MovieList } from "@/components/molecules/MovieList";
|
||||||
|
import { GenreList } from "@/components/molecules/GenreList";
|
||||||
|
|
||||||
// 12 hours
|
// 12 hours
|
||||||
export const revalidate = 43200;
|
export const revalidate = 43200;
|
||||||
|
|
@ -88,6 +89,8 @@ export default async function Home() {
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<GenreList heading="Odkrywaj filmy według gatunku" />
|
||||||
|
|
||||||
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { GenreList } from "@/components/molecules/GenreList";
|
||||||
import { MovieList } from "@/components/molecules/MovieList";
|
import { MovieList } from "@/components/molecules/MovieList";
|
||||||
import { TrackedMovies } from "@/components/molecules/TrackedMovies";
|
import { TrackedMovies } from "@/components/molecules/TrackedMovies";
|
||||||
|
|
||||||
|
|
@ -6,6 +7,7 @@ export default async function Home() {
|
||||||
<>
|
<>
|
||||||
<TrackedMovies />
|
<TrackedMovies />
|
||||||
<MovieList heading="Moja lista" />
|
<MovieList heading="Moja lista" />
|
||||||
|
<GenreList heading="Odkrywaj nowe filmy według gatunku" />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
|
import Link from "next/link";
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
genre: string;
|
genre: string;
|
||||||
|
id: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GenreLabel: FC<Props> = ({ genre }) => {
|
export const GenreLabel: FC<Props> = ({ genre, id }) => {
|
||||||
return (
|
return (
|
||||||
<span className="px-3 py-1 bg-gradient-to-r from-purple-600/30 to-pink-600/30 rounded-full text-sm border border-purple-400/30">
|
<span className="px-3 py-1 bg-gradient-to-r from-purple-600/30 to-pink-600/30 rounded-full text-sm border border-purple-400/30">
|
||||||
{genre}
|
<Link href={`/odkrywaj/gatunek/${id}`}>{genre}</Link>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
import Link from "next/link";
|
||||||
|
import { getMovieGenres } from "@/lib/tmdb/server";
|
||||||
|
import { Genre } from "@/lib/tmdb/types";
|
||||||
|
import { FC } from "react";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
heading?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Genre color mappings for visual variety.
|
||||||
|
const getGenreStyle = (genreId: number) => {
|
||||||
|
const styles = [
|
||||||
|
"from-purple-600/30 to-pink-600/30 border-purple-400/30 hover:from-purple-500/40 hover:to-pink-500/40 hover:border-purple-300/40 hover:shadow-purple-500/20",
|
||||||
|
"from-cyan-600/30 to-blue-600/30 border-cyan-400/30 hover:from-cyan-500/40 hover:to-blue-500/40 hover:border-cyan-300/40 hover:shadow-cyan-500/20",
|
||||||
|
"from-emerald-600/30 to-teal-600/30 border-emerald-400/30 hover:from-emerald-500/40 hover:to-teal-500/40 hover:border-emerald-300/40 hover:shadow-emerald-500/20",
|
||||||
|
"from-rose-600/30 to-red-600/30 border-rose-400/30 hover:from-rose-500/40 hover:to-red-500/40 hover:border-rose-300/40 hover:shadow-rose-500/20",
|
||||||
|
"from-amber-600/30 to-orange-600/30 border-amber-400/30 hover:from-amber-500/40 hover:to-orange-500/40 hover:border-amber-300/40 hover:shadow-amber-500/20",
|
||||||
|
"from-indigo-600/30 to-purple-600/30 border-indigo-400/30 hover:from-indigo-500/40 hover:to-purple-500/40 hover:border-indigo-300/40 hover:shadow-indigo-500/20",
|
||||||
|
];
|
||||||
|
return styles[genreId % styles.length];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GenreList: FC<Props> = async ({ heading = "Gatunki filmowe" }) => {
|
||||||
|
const { genres } = await getMovieGenres();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="blocks">
|
||||||
|
<div className="container">
|
||||||
|
<h2 className="text-3xl font-bold mb-8 text-center bg-gradient-to-r from-purple-400 via-pink-400 to-cyan-400 bg-clip-text text-transparent">
|
||||||
|
{heading}
|
||||||
|
</h2>
|
||||||
|
<div className="flex flex-wrap gap-4 justify-center">
|
||||||
|
{genres.map((genre: Genre) => (
|
||||||
|
<Link
|
||||||
|
key={genre.id}
|
||||||
|
href={`/odkrywaj/gatunek/${genre.id}`}
|
||||||
|
className={`group relative overflow-hidden rounded-2xl px-4 py-2 text-center
|
||||||
|
transition-all duration-500 hover:scale-105 hover:-translate-y-1
|
||||||
|
bg-gradient-to-br ${getGenreStyle(genre.id)}
|
||||||
|
border shadow-lg hover:shadow-2xl`}
|
||||||
|
>
|
||||||
|
{/* Subtle aurora glow effect */}
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-br from-white/5 via-transparent to-white/5 opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<div className="relative z-10">
|
||||||
|
<span className="text-sm font-semibold text-white/90 group-hover:text-white transition-colors duration-300">
|
||||||
|
{genre.name}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Optional decorative element */}
|
||||||
|
<div className="mt-12 flex justify-center">
|
||||||
|
<div className="w-32 h-px bg-gradient-to-r from-transparent via-purple-400/50 to-transparent" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -174,7 +174,11 @@ export const HeroMovie: FC<Props> = ({ movieDetails }) => {
|
||||||
</h3>
|
</h3>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{movieDetails.genres.map((genre) => (
|
{movieDetails.genres.map((genre) => (
|
||||||
<GenreLabel key={genre.id} genre={genre.name} />
|
<GenreLabel
|
||||||
|
key={genre.id}
|
||||||
|
genre={genre.name}
|
||||||
|
id={genre.id}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,10 @@
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SearchResult,
|
SearchResult,
|
||||||
MovieDetails,
|
|
||||||
MovieCredits,
|
MovieCredits,
|
||||||
MovieDetailsRich,
|
MovieDetailsRich,
|
||||||
PersonDetails,
|
|
||||||
PersonMovieCredits,
|
|
||||||
PersonImages,
|
|
||||||
PersonDetailsRich,
|
PersonDetailsRich,
|
||||||
|
Genre,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
const url = "https://api.themoviedb.org/3";
|
const url = "https://api.themoviedb.org/3";
|
||||||
|
|
@ -88,3 +85,7 @@ export async function getPersonDetails(
|
||||||
`/person/${personId}?language=pl&append_to_response=movie_credits,images,external_ids`
|
`/person/${personId}?language=pl&append_to_response=movie_credits,images,external_ids`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getMovieGenres(): Promise<{ genres: Genre[] }> {
|
||||||
|
return await fetchTmbd("/genre/movie/list?language=pl");
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue