Refactor MovieList component to support multiple movie categories with sorting and filtering options; update Home page to display categorized movie lists; enhance global styles for grid layout.

This commit is contained in:
Norbert Maciaszek 2025-08-05 22:10:22 +02:00
parent 2359cfc582
commit b08cdea130
4 changed files with 95 additions and 11 deletions

View File

@ -43,3 +43,7 @@
width: calc(--value(integer) / 0.12 * 1%); width: calc(--value(integer) / 0.12 * 1%);
padding: 0 15px; padding: 0 15px;
} }
@utility grid-auto-cols-* {
grid-template-columns: repeat(auto-fill, --value(integer));
}

View File

@ -5,7 +5,15 @@ export default async function Home() {
return ( return (
<main> <main>
<SearchMovies /> <SearchMovies />
<MovieList /> <MovieList
heading="Upcoming"
onlyUpcoming
sort="releaseDate"
sortDirection="desc"
/>
<MovieList heading="My Watchlist" onlyReleased showFilters />
<MovieList heading="Seen" onlySeen />
<MovieList heading="Favorites" onlyFavorites />
</main> </main>
); );
} }

View File

@ -1,21 +1,92 @@
"use client"; "use client";
import { FC, useState } from "react";
import { MovieCard } from "@/components/atoms/MovieCard"; import { MovieCard } from "@/components/atoms/MovieCard";
import { useGlobalStore } from "@/app/store/globalStore"; import { useGlobalStore } from "@/app/store/globalStore";
export const MovieList = () => { type Props = {
heading: string;
onlySeen?: boolean;
onlyFavorites?: boolean;
onlyUpcoming?: boolean;
onlyReleased?: boolean;
showFilters?: boolean;
sort?: "title" | "releaseDate" | "popularity";
sortDirection?: "asc" | "desc";
};
export const MovieList: FC<Props> = ({
heading,
onlyFavorites,
onlySeen,
onlyUpcoming,
onlyReleased,
showFilters = false,
sort = "title",
sortDirection = "asc",
}) => {
const [filter, setFilter] = useState<"title" | "releaseDate" | "popularity">(
sort
);
const { movies } = useGlobalStore(); const { movies } = useGlobalStore();
const filteredMovies = movies.filter((movie) => {
if (onlySeen) return movie.seen === 1;
if (onlyFavorites) return movie.favorite === 1;
if (onlyUpcoming) return new Date(movie.releaseDate) > new Date();
if (onlyReleased) return new Date(movie.releaseDate) < new Date();
return true;
});
let sortedMovies = filteredMovies.sort((a, b) => {
if (filter === "title") return a.title.localeCompare(b.title);
if (filter === "releaseDate")
return (
new Date(b.releaseDate).getTime() - new Date(a.releaseDate).getTime()
);
if (filter === "popularity") return b.popularity - a.popularity;
return 0;
});
if (sortDirection === "desc") {
sortedMovies = sortedMovies.reverse();
}
const handleSort = (sort: "title" | "releaseDate" | "popularity") => {
setFilter(sort);
};
return ( return (
<section className="my-6"> <section className="my-20">
<div className="container"> <div className="container">
<h2 className="text-2xl font-bold">My Watchlist</h2> <div className="row">
{movies.length === 0 && ( <div className="col-12 md:col-10">
<h2 className="text-2xl font-bold">{heading}</h2>
</div>
{showFilters && (
<div className="col-12 md:col-2">
<select
className="bg-accent/70 text-white px-4 py-2 rounded-md w-full hover:bg-primary transition-colors cursor-pointer"
value={filter}
onChange={(e) =>
handleSort(
e.target.value as "title" | "releaseDate" | "popularity"
)
}
>
<option value="title">Title</option>
<option value="releaseDate">Release Date</option>
<option value="popularity">Popularity</option>
</select>
</div>
)}
</div>
{filteredMovies.length === 0 && (
<p className="text-text/60 text-sm">No movies found</p> <p className="text-text/60 text-sm">No movies found</p>
)} )}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-6 mt-8"> {filteredMovies.length > 0 && (
{movies <div className="grid grid-auto-cols-282 gap-6 mt-8 justify-center">
.sort((a, b) => a.title.localeCompare(b.title)) {sortedMovies.map((movie) => (
.map((movie) => (
<MovieCard <MovieCard
key={movie.id} key={movie.id}
{...movie} {...movie}
@ -24,7 +95,8 @@ export const MovieList = () => {
favorite={movie.favorite === 1} favorite={movie.favorite === 1}
/> />
))} ))}
</div> </div>
)}
</div> </div>
</section> </section>
); );

View File

@ -14,7 +14,7 @@ export const SearchMovies = () => {
}; };
return ( return (
<section className="my-6"> <section className="mb-20">
<div className="container"> <div className="container">
<div className="row justify-center"> <div className="row justify-center">
<div className="col-5"> <div className="col-5">